Smoothing

Welcome to the exercises for the second ggplot2 course!

To practice on the remaining four layers (statistics, coordinates, facets and themes), we’ll continue working on several datasets that we already encountered in the first course.

The mtcars dataset contains information for 32 cars from Motor Trends magazine from 1973. This dataset is small, intuitive, and contains a variety of continuous and categorical (both nominal and ordinal) variables.

In the previous course we learned how to effectively use some basic geometries, such as point, bar and line. In the first chapter of this course we’ll explore statistics associated with specific geoms, for example, smoothing and lines.

library(ggplot2)
Need help? Try the ggplot2 mailing list: http://groups.google.com/group/ggplot2.
# Explore the mtcars data frame with str()
str(mtcars)
'data.frame':   32 obs. of  12 variables:
 $ mpg : num  21 21 22.8 21.4 18.7 18.1 14.3 24.4 22.8 19.2 ...
 $ cyl : Factor w/ 3 levels "4","6","8": 2 2 1 2 3 2 3 1 1 2 ...
 $ disp: num  160 160 108 258 360 ...
 $ hp  : num  110 110 93 110 175 105 245 62 95 123 ...
 $ drat: num  3.9 3.9 3.85 3.08 3.15 2.76 3.21 3.69 3.92 3.92 ...
 $ wt  : num  2.62 2.88 2.32 3.21 3.44 ...
 $ qsec: num  16.5 17 18.6 19.4 17 ...
 $ vs  : num  0 0 1 1 0 1 0 1 1 1 ...
 $ am  : num  1 1 1 0 0 0 0 0 0 0 ...
 $ gear: num  4 4 4 3 3 3 3 4 4 4 ...
 $ carb: num  4 4 1 1 2 1 4 2 2 4 ...
 $ fcyl: Factor w/ 3 levels "4","6","8": 2 2 1 2 3 2 3 1 1 2 ...
# A scatter plot with LOESS smooth:
ggplot(mtcars, aes(x = wt, y = mpg)) +
  geom_point() +
  geom_smooth()

# A scatter plot with an ordinary Least Squares linear model:
ggplot(mtcars, aes(x = wt, y = mpg)) +
  geom_point() +
  geom_smooth(method="lm")

# The previous plot, without CI ribbon:
ggplot(mtcars, aes(x = wt, y = mpg)) +
  geom_point() +
  geom_smooth(method = "lm", se = FALSE)

# The previous plot, without points:
ggplot(mtcars, aes(x = wt, y = mpg)) +
  geom_smooth(method = "lm", se=FALSE)

Grouping variables We’ll continue with the previous exercise by considering the situation of looking at sub-groups in our dataset. For this we’ll encounter the invisible group aesthetic.

# ggplot2 is already loaded
# Define cyl as a factor variable
ggplot(mtcars, aes(x = wt, y = mpg, col = factor(cyl))) +
  geom_point() +
  stat_smooth(method = "lm", se = F)

# Complete the following ggplot command as instructed
ggplot(mtcars, aes(x = wt, y = mpg, col = factor(cyl))) +
  geom_point() +
  stat_smooth(method = "lm", se = F) +
  stat_smooth(aes(group =1 ), method = "lm", se = F)

Modifying stat_smooth In the previous exercise we saw how to modify the smooth aesthetic by removing the 95% Confidence Interval. Here we’ll consider the span for LOESS smoothing and we’ll take a look at a nice scenario of how to properly map our different models.

ggplot2 is already loaded and several of the linear models we looked at in the two previous exercises are already given.

# Plot 1: change the LOESS span
ggplot(mtcars, aes(x = wt, y = mpg)) +
  geom_point() +
  # Add span below
  geom_smooth(se = F, span=0.7)

# Plot 2: Set the overall model to LOESS and use a span of 0.7
ggplot(mtcars, aes(x = wt, y = mpg, col = factor(cyl))) +
  geom_point() +
  stat_smooth(method = "lm", se = F) +
  # Change method and add span below
  stat_smooth(method = "loess", aes(group = 1),
              se = F, col = "black", span=0.7)

# Plot 3: Set col to "All", inside the aes layer of stat_smooth()
ggplot(mtcars, aes(x = wt, y = mpg, col = factor(cyl))) +
  geom_point() +
  stat_smooth(method = "lm", se = F) +
  stat_smooth(method = "loess",
              # Add col inside aes()
              aes(group = 1,col = "All"),
              # Remove the col argument below
              se = F, span = 0.7)

# Plot 4: Add scale_color_manual to change the colors
myColors <- c("#1B9E77", "#D95F02", "#7570B3", "black")
ggplot(mtcars, aes(x = wt, y = mpg, col = factor(cyl))) +
  geom_point() +
  stat_smooth(method = "lm", se = F, span = 0.75) +
  stat_smooth(method = "loess",
              aes(group = 1, col="All"),
              se = F, span = 0.7) +
  # Add correct arguments to scale_color_manual
  scale_color_manual("Cylinders", values=myColors)

Modifying stat_smooth (2) In this exercise we’ll take a look at a more subtle example of defining and using linear models. ggplot2 and the Vocab data frame are already loaded for you.

library(car)
library(RColorBrewer)
data(Vocab)
# Plot 1: Jittered scatter plot, add a linear model (lm) smooth:
ggplot(Vocab, aes(x = education, y = vocabulary)) +
  geom_jitter(alpha = 0.2) +
  stat_smooth(method = "lm", se=FALSE)

# Plot 2: Only lm, colored by year
ggplot(Vocab, aes(x = education, y = vocabulary, col=factor(year))) +
  stat_smooth(method = "lm", se=FALSE)

# Plot 3: Set a color brewer palette
ggplot(Vocab, aes(x = education, y = vocabulary, col=factor(year))) +
  stat_smooth(method = "lm", se=FALSE) +
  scale_color_brewer("Blues")

# Plot 4: Add the group, specify alpha and size
ggplot(Vocab, aes(x = education, y = vocabulary, col = year, group=factor(year))) +
  stat_smooth(method = "lm", se = F, alpha=0.6, size=2) +
  scale_color_gradientn(colors = brewer.pal(9,"YlOrRd"))

Quantiles The previous example used the Vocab dataset and applied linear models describing vocabulary by education for different years. Here we’ll continue with that example by using stat_quantile() to apply a quantile regression (method rq).

By default, the 1st, 2nd (i.e. median), and 3rd quartiles are modeled as a response to the predictor variable, in this case education. Specific quantiles can be specified with the quantiles argument.

If you want to specify many quantile and color according to year, then things get too busy. We’ll explore ways of dealing with this in the next chapter.

library(SparseM)
# Use stat_quantile instead of stat_smooth:
ggplot(Vocab, aes(x = education, y = vocabulary, col = year, group = factor(year))) +
  stat_quantile(alpha = 0.6, size = 2) +
  scale_color_gradientn(colors = brewer.pal(9,"YlOrRd"))

# Set quantile to 0.5:
ggplot(Vocab, aes(x = education, y = vocabulary, col = year, group = factor(year))) +
  stat_quantile(alpha = 0.6, size = 2, quantiles = 0.5) +
  scale_color_gradientn(colors = brewer.pal(9,"YlOrRd"))

Sum Another useful stat function is stat_sum() which calculates the count for each group.

# Plot with linear and loess model
p <- ggplot(Vocab, aes(x = education, y = vocabulary)) +
       stat_smooth(method = "loess", aes(col = "x"), se = F) +
       stat_smooth(method = "lm", aes(col = "y"), se = F) +
       scale_color_discrete("Model", labels = c("x" = "LOESS", "y" = "lm"))
# Add stat_sum
p + stat_sum()

# Add stat_sum and set size range
p + stat_sum() + scale_size(range = c(1,10))

Preparations Here we’ll look at stat_summary() in action. We’ll build up various plots one-by-one.

In this exercise we’ll consider the preparations. That means we’ll make sure the data is in the right format and that all the positions that we might use in our plots are defined. Lastly, we’ll set the base layer for our plot. ggplot2 is already loaded, so you can get started straight away!

# Display structure of mtcars
str(mtcars)
'data.frame':   32 obs. of  12 variables:
 $ mpg : num  21 21 22.8 21.4 18.7 18.1 14.3 24.4 22.8 19.2 ...
 $ cyl : Factor w/ 3 levels "4","6","8": 2 2 1 2 3 2 3 1 1 2 ...
 $ disp: num  160 160 108 258 360 ...
 $ hp  : num  110 110 93 110 175 105 245 62 95 123 ...
 $ drat: num  3.9 3.9 3.85 3.08 3.15 2.76 3.21 3.69 3.92 3.92 ...
 $ wt  : num  2.62 2.88 2.32 3.21 3.44 ...
 $ qsec: num  16.5 17 18.6 19.4 17 ...
 $ vs  : num  0 0 1 1 0 1 0 1 1 1 ...
 $ am  : num  1 1 1 0 0 0 0 0 0 0 ...
 $ gear: num  4 4 4 3 3 3 3 4 4 4 ...
 $ carb: num  4 4 1 1 2 1 4 2 2 4 ...
 $ fcyl: Factor w/ 3 levels "4","6","8": 2 2 1 2 3 2 3 1 1 2 ...
# Convert cyl and am to factors:
mtcars$cyl <- as.factor(mtcars$cyl)
mtcars$am <- as.factor(mtcars$am)
# Define positions:
posn.d <- position_dodge(width = 0.1)
posn.jd <- position_jitterdodge(jitter.width = 0.1, dodge.width = 0.2)
posn.j <- position_jitter(width = 0.2)
# base layers:
wt.cyl.am <- ggplot(mtcars, aes(x=cyl, y=wt, col=am, fill= am, group=am))

Plotting variations Now that the preparation work is done, let’s have a look at at stat_summary().

ggplot2 is already loaded, as is wt.cyl.am, which is defined as

wt.cyl.am <- ggplot(mtcars, aes(x = cyl, y = wt, col = am, fill = am, group = am)) Also all the position objects of the previous exercise, posn.d, posn.jd and posn.j, are available. For starters, Plot 1 is already coded for you.

library(Hmisc)
Loading required package: lattice
Loading required package: survival

Attaching package: 'survival'

The following object is masked from 'package:quantreg':

    untangle.specials

Loading required package: Formula

Attaching package: 'Hmisc'

The following object is masked from 'package:quantreg':

    latex

The following objects are masked from 'package:base':

    format.pval, units
# wt.cyl.am, posn.d, posn.jd and posn.j are available
# Plot 1: Jittered, dodged scatter plot with transparent points
wt.cyl.am +
  geom_point(position = posn.jd, alpha = 0.6)

# Plot 2: Mean and SD - the easy way
wt.cyl.am +
  geom_point(position = posn.jd, alpha = 0.6) +
  stat_summary(fun.data = mean_sdl, fun.args = list(mult=1), position = posn.d)

# Plot 3: Mean and 95% CI - the easy way
wt.cyl.am +
  geom_point(position = posn.jd, alpha = 0.6) +
  stat_summary(fun.data = mean_cl_normal, position = posn.d)

# Plot 4: Mean and SD - with T-tipped error bars - fill in ___
wt.cyl.am +
  stat_summary(geom = "point", fun.y = mean,
               position = posn.d) +
  stat_summary(geom = "errorbar", fun.data = mean_sdl,
               position = posn.d, fun.args = list(mult = 1), width = 0.1)

Custom Functions In the video we saw that the only difference between ggplot2::mean_sdl() and Hmisc::smean.sdl() is the naming convention. In order to use the results of a function directly in ggplot2 we need to ensure that the names of the variables match the aesthetics needed for our respective geoms.

Here we’ll create two new functions in order to create the plot shown in the viewer. One function will measure the full range of the dataset and the other will measure the interquartile range.

A play vector, xx, has been created for you. Execute

mean_sdl(xx, mult = 1) in the R Console and consider the format of the output. You’ll have to produce functions which return similar outputs.

xx = c(1:100)
# Play vector xx is available
# Function to save range for use in ggplot
gg_range <- function(x) {
  # Change x below to return the instructed values
  data.frame(ymin = min(x), # Min
             ymax = max(x)) # Max
}
gg_range(xx)
# Required output:
#   ymin ymax
# 1    1  100
# Function to Custom function:
med_IQR <- function(x) {
  # Change x below to return the instructed values
  data.frame(y = median(x), # Median
             ymin = quantile(x)[2], # 1st quartile
             ymax = quantile(x)[4])  # 3rd quartile
}
med_IQR(xx)
# Required output:
#        y  ymin  ymax
# 25% 50.5 25.75 75.25

Custom Functions (2) In the last exercise we created functions that will allow us to plot the so-called five-number summary (the minimum, 1st quartile, median, 3rd quartile, and the maximum). Here, we’ll implement that into a unique plot type.

All the functions and objects from the previous exercise are available including the updated mtcars data frame, the position object posn.d, the base layers wt.cyl.am and the functions med_IQR() and gg_range().

The plot you’ll end up with at the end of this exercise is shown on the right.

# The base ggplot command, you don't have to change this
wt.cyl.am <- ggplot(mtcars, aes(x = cyl,y = wt, col = am, fill = am, group = am))
# Add three stat_summary calls to wt.cyl.am
wt.cyl.am +
  stat_summary(geom = "linerange", fun.data = med_IQR,
               position = posn.d, size = 3) +
  stat_summary(geom = "linerange", fun.data = gg_range,
               position = posn.d, size = 3,
               alpha = 0.4) +
  stat_summary(geom = "point", fun.y = median,
               position = posn.d, size = 3,
               col = "black", shape = "X")

Zooming In In the video, you saw different ways of using the coordinates layer to zoom in. In this exercise, we’ll compare some of the techniques again.

As usual, you’ll be working with the mtcars dataset, which is already cleaned up for you (cyl and am are categorical variables). Also p, a ggplot object you coded in the previous chapter, is already available. Execute p in the console to check it out.

# Basic ggplot() command, coded for you
p <- ggplot(mtcars, aes(x = wt, y = hp, col = am)) + geom_point() + geom_smooth()
# Add scale_x_continuous
p + scale_x_continuous(limits = c(3,6), expand=c(0,0))

# The proper way to zoom in:
p + coord_cartesian(xlim = c(3,6))

Aspect Ratio We can set the aspect ratio of a plot with coord_fixed() or coord_equal(). Both use aspect = 1 as a default. A 1:1 aspect ratio is most appropriate when two continuous variables are on the same scale, as with the iris dataset.

All variables are measured in centimeters, so it only makes sense that one unit on the plot should be the same physical distance on each axis. This gives a more truthful depiction of the relationship between the two variables since the aspect ratio can change the angle of our smoothing line. This would give an erroneous impression of the data.

Of course the underlying linear models don’t change, but our perception can be influenced by the angle drawn.

# Complete basic scatter plot function
base.plot <- ggplot(iris, aes(x = Sepal.Length, y=Sepal.Width, col=Species)) +
               geom_jitter() +
               geom_smooth(method = "lm", se = F)
# Plot base.plot: default aspect ratio
base.plot

# Fix aspect ratio (1:1) of base.plot
base.plot +
coord_equal()

Pie Charts The coord_polar() function converts a planar x-y cartesian plot to polar coordinates. This can be useful if you are producing pie charts.

We can imagine two forms for pie charts - the typical filled circle, or a colored ring.

As an example, consider the stacked bar chart shown in the viewer. Imagine that we just take the y axis on the left and bend it until it loops back on itself, while expanding the right side as we go along. We’d end up with a pie chart - it’s simply a bar chart transformed onto a polar coordinate system.

Typical pie charts omit all of the non-data ink, which we’ll learn about in the next chapter. Pie charts are not really better than stacked bar charts, but we’ll come back to this point in the fourth chapter on best practices.

The mtcars data frame is available, with cyl converted to a factor for you.

# Create stacked bar plot: thin.bar
thin.bar <- ggplot(mtcars, aes(x=1, fill = cyl)) +
              geom_bar()
# Convert thin.bar to pie chart
thin.bar + coord_polar(theta = "y")

# Create stacked bar plot: wide.bar
wide.bar <- ggplot(mtcars, aes(x=1, fill = cyl)) +
              geom_bar(width = 1)
# Convert wide.bar to pie chart
wide.bar + coord_polar(theta = "y")

Facets: the basics The most straightforward way of using facets is facet_grid(). Here we just need to specify the categorical variable to use on rows and columns using formula notation.

Notice that we can also take advantage of ordinal variables by positioning them in the correct order as columns or rows, as is the case with the number of cylinders. Get some hands-on practice in this exercise; ggplot2 is already loaded for you. mtcars is available where cyl and am are factors.

# Basic scatter plot:
p <- ggplot(mtcars, aes(x = wt, y = mpg)) +
  geom_point()
# Separate rows according to transmission type, am
p + facet_grid(am ~ .)

# Separate columns according to cylinders, cyl
p + facet_grid(. ~ cyl)

# Separate by both columns and rows 
p + facet_grid(am ~ cyl)

Many variables Facets are another way of presenting categorical variables. Recall that we saw all the ways of combining variables, both categorical and continuous, in the aesthetics chapter. Sometimes it’s possible to overdo it. Here we’ll present a plot with 6 variables and see if we can add even more.

Let’s begin by using a trick to map two variables onto two color scales - hue and lightness. We combine cyl and am into a single variable cyl_am. To accommodate this we also make a new color palette with alternating red and blue of increasing darkness. This is saved as myCol

# Code to create the cyl_am col and myCol vector
mtcars$cyl_am <- paste(mtcars$cyl, mtcars$am, sep = "_")
myCol <- rbind(brewer.pal(9, "Blues")[c(3,6,8)],
               brewer.pal(9, "Reds")[c(3,6,8)])
# Basic scatter plot, add color scale:
ggplot(mtcars, aes(x = wt, y = mpg, col = cyl_am)) +
  geom_point() +
  scale_color_manual(values = myCol)

  
# Facet according on rows and columns.
ggplot(mtcars, aes(x = wt, y = mpg, col = cyl_am)) +
  geom_point() +
  scale_color_manual(values = myCol) +
  facet_grid(gear ~ vs)

# Add more variables
ggplot(mtcars, aes(x = wt, y = mpg, size = disp, col = cyl_am)) +
  geom_point() +
  scale_color_manual(values = myCol) +
  facet_grid(gear ~ vs)

Dropping levels When you have a categorical variable with many levels which are not all present in sub-groups of another variable, it may be desirable to drop the unused levels. As an example let’s return to the mammalian sleep dataset, mamsleep. It is available in your workspace.

The variables of interest here are name, which contains the full popular name of each animal, and vore, the eating behavior. Each animal can only be classified under one eating habit, so if we facet according to vore, we don’t need to repeat the full list in each sub-plot.

# Basic scatter plot
# Basic scatter plot
ggplot(msleep, aes(x = sleep_total, y = name)) +
  geom_point()

# Facet rows accoding to vore
ggplot(msleep, aes(x = sleep_total, y = name)) +
  geom_point() +
  facet_grid(vore ~ .)

# Specify scale and space arguments to free up rows
ggplot(msleep, aes(x = sleep_total, y = name)) +
  geom_point() +
  facet_grid(vore ~ ., scale="free_y", space="free_y")

Rectangles To understand all the arguments for the themes, you’ll modify an existing plot over the next series of exercises.

Here you’ll focus on the rectangles of the plotting object z that has already been created for you. If you type z in the console, you can check it out. The goal is to turn z into the plot in the viewer. Do this by following the instructions step by step.

z <- ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width, col = Species)) +
 geom_jitter(alpha = 0.7) +
 scale_color_brewer("Species",
 palette = "Dark2",
 labels = c("Setosa",
 "Versicolor",
"Virginica")) +
 scale_y_continuous("Width (cm)", limits = c(2, 4.5), expand = c(0, 0)) +
 scale_x_continuous("Length (cm)", limits = c(4, 8), expand = c(0, 0)) +
 ggtitle("Sepals") +
 coord_fixed(1)
myPink = "#FEE0D2"
# Plot 1: change the plot background color to myPink:
z + theme(plot.background = element_rect(fill = myPink))

# Plot 2: adjust the border to be a black line of size 3
z + theme(plot.background = element_rect(fill = myPink, color="black", size = 3))

# Plot 3: set panel.background, legend.key, legend.background and strip.background to element_blank()
uniform_panels <- theme(panel.background = element_blank(), 
                        legend.key = element_blank(), 
                        legend.background=element_blank(), 
                        strip.background = element_blank())
z + theme(plot.background = element_rect(fill = myPink, color="black", size = 3)) + uniform_panels

Lines To continue exploring custom plot modifications, we turn now to lines. To change the appearance of lines use the element_line() function.

The plot you created in the last exercise, with the fancy pink background, is available as the plotting object z. Your goal is to produce the plot in the viewer.

z + theme(panel.grid = element_blank(), axis.line = element_line(color = "black"), axis.ticks = element_line(color = "black"))

Text Next we can make the text on your plot prettier and easier to spot. You can do this through the element_text() function and by passing the appropriate arguments inside the theme() function.

As before, the plot you’ve created in the previous exercise is available as z. The plot you should end up with after successfully completing this exercises is shown in the viewer.

myRed = "#99000D"
z + theme(strip.text = element_text(size = 16, color = myRed), axis.title.y= element_text(color=myRed, hjust = 0, face = "italic"), axis.title.x = element_text(color = myRed, hjust = 0, face = "italic"), axis.text = element_text(color = "black"))

Legends ggplot2 is very good at creating appropriate legends to go with your plot. Of course, the themes layer also allows you to specify the appearance and location of these legends. In this exercise, you’ll experiment with some techniques to move your legend around and change its structure.

The plot you’ve coded up to now is available as z. It’s also displayed in the viewer. Solve the instructions and compare the resulting plots with the plot you started with.

# Move legend by position
z + theme(legend.position = c(0.85, 0.85))

# Change direction
z + theme(legend.direction = "horizontal")

# Change location by name
z + theme(legend.position = "bottom")

# Remove legend entirely
z + theme(legend.position = "none")

Positions The different rectangles of your plot have spacing between them. There’s spacing between the facets, between the axis labels and the plot rectangle, between the plot rectangle and the entire panel background, etc. Let’s experiment!

The last plot you created in the previous exercise, without a legend, is available as z.

library(grid)
#Increase spacing between facets
z + theme(panel.spacing.x=unit(2, "cm"))

# Add code to remove any excess plot margin space
z + theme(panel.spacing.x=unit(2, "cm"), plot.margin = unit(c(0,0,0,0), "cm"))

Update Themestheme update Building your themes every time from scratch can become a pain and unnecessarily bloat your scripts. In the following exercises, you will go through some challenges to practice with the different ways of managing, updating and saving themes.

A plotting object z2 is already created for you on the right. It shows mpg against wt for the mtcars dataset, faceted according to cyl. Also the colors myPink and myRed are available. In the previous exercises you’ve already customized the rectangles, lines and text on the plot. This theme layer is now separately stored as theme_pink, as shown in the sample code.

# Theme layer saved as an object, theme_pink
theme_pink <- theme(panel.background = element_blank(),
                    legend.key = element_blank(),
                    legend.background = element_blank(),
                    strip.background = element_blank(),
                    plot.background = element_rect(fill = myPink, color = "black", size = 3),
                    panel.grid = element_blank(),
                    axis.line = element_line(color = "black"),
                    axis.ticks = element_line(color = "black"),
                    strip.text = element_text(size = 16, color = myRed),
                    axis.title.y = element_text(color = myRed, hjust = 0, face = "italic"),
                    axis.title.x = element_text(color = myRed, hjust = 0, face = "italic"),
                    axis.text = element_text(color = "black"),
                    legend.position = "none")
  
# Apply theme_pink to z2
z + theme_pink

# Change code so that old theme is saved as old
old <- theme_update(panel.background = element_blank(),
             legend.key = element_blank(),
             legend.background = element_blank(),
             strip.background = element_blank(),
             plot.background = element_rect(fill = myPink, color = "black", size = 3),
             panel.grid = element_blank(),
             axis.line = element_line(color = "black"),
             axis.ticks = element_line(color = "black"),
             strip.text = element_text(size = 16, color = myRed),
             axis.title.y = element_text(color = myRed, hjust = 0, face = "italic"),
             axis.title.x = element_text(color = myRed, hjust = 0, face = "italic"),
             axis.text = element_text(color = "black"),
             legend.position = "none")
# Display the plot z2
z

# Restore the old plot
theme_set(old)

Exploring ggthemes There are many themes available by default in ggplot2: theme_bw(), theme_classic(), theme_gray(), etc. In the previous exercise, you saw that you can apply these themes to all following plots, with theme_set():

theme_set(theme_bw()) But you can also apply them on a particular plot, with:

… + theme_bw() Next, it’s perfectly possible and super-easy to extend these themes with your own modifications. In this exercise, you will experiment with this and use some preset templates available from the ggthemes package. The workspace already contains the same basic plot from before under the name z2.

# Load ggthemes package
library(ggthemes)
package 'ggthemes' was built under R version 3.4.4
# Apply theme_tufte
z + theme_tufte()

# Apply theme_tufte, modified:
z + theme_tufte() +
  theme(legend.position = c(0.9, 0.9),
  legend.title = element_text(face = "italic", size = 12), axis.title = element_text(face = "bold", size = 14))

NA

Bar Plots (1) In the video we saw why “dynamite plots” (bar plots with error bars) are not well suited for their intended purpose of depicting distributions. If you really want error bars on bar plots, you can still get that. However, you’ll need to set the positions manually. A point geom will typically serve you much better.

We saw an example of a dynamite plot earlier in this course. Let’s return to that code and make sure you know how to handle it. We’ll use the mtcars dataset for examples. The first part of this exercise will just be a refresher, then we’ll get into some details.

m <- ggplot(mtcars, aes(x = cyl, y = wt))
# Draw dynamite plot
m +
  stat_summary(fun.y = mean, geom = "bar", fill = "skyblue") +
  stat_summary(fun.data = mean_sdl, fun.args = list(mult = 1), geom = "errorbar", width = 0.1)

Bar Plots (2) In the previous exercise we used the mtcars dataset to draw a dynamite plot about the weight of the cars per cylinder type.

In this exercise we will add a distinction between transmission type, am, for the dynamite plots.

# Base layers
m <- ggplot(mtcars, aes(x = cyl,y = wt, col = am, fill = am))
# Plot 1: Draw dynamite plot
m +
  stat_summary(fun.y = mean, geom = "bar") +
  stat_summary(fun.data = mean_sdl, fun.args = list(mult = 1), geom = "errorbar", width = 0.1)

# Plot 2: Set position dodge in each stat function
m +
  stat_summary(fun.y = mean, geom = "bar", position = "dodge") +
  stat_summary(fun.data = mean_sdl, fun.args = list(mult = 1), 
               geom = "errorbar", width = 0.1, position = "dodge")

# Set your dodge posn manually
posn.d <- position_dodge(0.9)
# Plot 3:  Redraw dynamite plot
m +
  stat_summary(fun.y = mean, geom = "bar", position = posn.d) +
  stat_summary(fun.data = mean_sdl, fun.args = list(mult = 1), geom = "errorbar", width = 0.1, position = posn.d)

Bar Plots (3) If it is appropriate to use bar plots (see the video for a discussion), then it would also be nice to give an impression of the number of values in each group.

stat_summary() doesn’t keep track of the count. stat_sum() does (that’s the whole point), but it’s difficult to access. In this case, the most straightforward thing to do is calculate exactly what we want to plot beforehand. For this exercise we’ve created a summary data frame called mtcars.cyl which contains the average (wt.avg), standard deviations (sd) and count (n) of car weights, according to cylinders, cyl. It also contains the proportion (prop) of each cylinder represented in the entire dataset. Use the console to familiarize yourself with the mtcars.cyl data frame.

mtcars.cyl <- data.frame(
  cyl = c(4,6,8),
  wt.avg = c(2.285727, 3.117143, 3.999214),
  sd = c(0.569 ,0.356, 0.759),
  n  = c(11, 7,14),
  prop = c(0.34375, 0.21875, 0.43750)
)
# Base layers
m <- ggplot(mtcars.cyl, aes(x = cyl, y = wt.avg))
# Plot 1: Draw bar plot
m + geom_bar(stat = "identity", fill = "skyblue")

# Plot 2: Add width aesthetic
m + geom_bar(stat = "identity", fill = "skyblue", aes(width=prop)) 
Ignoring unknown aesthetics: width

  
# Plot 3: Add error bars
m + geom_bar(stat = "identity", fill = "skyblue", aes(width=prop)) +
geom_errorbar(aes(ymin = wt.avg - sd, ymax = wt.avg + sd), width=0.1)
Ignoring unknown aesthetics: width

Pie Charts (1) In this example we’re going to consider a typical use of pie charts - a categorical variable as the proportion of another categorical variable. For example, the proportion of each transmission type am, in each cylinder, cyl class.

The first plotting function in the editor should be familiar to you by now. It’s a straightforward bar chart with position = “fill”, as shown in the viewer. This is already a good solution to the problem at hand! Let’s take it one stap further and convert this plot in a pie chart.

# Convert bar chart to pie chart
ggplot(mtcars, aes(x = factor(1), fill = am)) +
  geom_bar(position = "fill", width=1) +
  facet_grid(.~cyl) +
  coord_polar(theta = "y")

Pie Charts (2) In the previous example, we looked at the proportions of one categorical variable (am) as a proportion of another (cyl). In this example, we’re interested in two, or possibly many, categorical variables independent of each other. Consider the pie charts in the viewer. This is an unsatisfactory visualization. What we’re interested in asking is the relationship between all these variables (e.g. where are 8 cylinder cars represented on the Transmission, Gear and Carburetor variables?) You can also imagine that we want to know the values of those cars on other scales, such as their weight. How can we combine all this information?

The trick is to use a parallel coordinates plot, like this one. Each variable is plotted on its own axis and they are plotted as parallel axes. The individual observations are connected with lines, which can be colored according to another variable. This is a surprisingly useful visualization since we can combine many variables, even if they are on entirely different scales.

A word of caution though: typically it is very taboo to draw lines in this way. It’s the reason why we don’t draw lines across levels of a nominal variable - the order, and thus the slope of the line, is meaningless. Parallel plots are a (very useful) exception to the rule!

# Parallel coordinates plot using GGally
library(GGally)
# All columns except am
group_by_am <- 9
my_names_am <- (1:11)[-group_by_am]
# Basic parallel plot - each variable plotted as a z-score transformation
ggparcoord(mtcars, my_names_am, groupColumn = group_by_am, alpha =0.8)

Heat Maps In the video you saw reasons for not using heat maps. Nonetheless, you may encounter a case in which you really do want to use one. Luckily, they’re fairly straightforward to produce in ggplot2.

We begin by specifying two categorical variables for the x and y aesthetics. At the intersection of each category we’ll draw a box, except here we call it a tile, using the geom_tile() layer. Then we will fill each tile with a continuous variable.

We’ll produce the heat map we saw in the video with the built-in barley dataset. The barley dataset is in the lattice package and has already been loaded for you. Begin by exploring the structure of the data in the console using str().

library(MASS)
# Create color palette
myColors <- brewer.pal(9, "Reds")
# Build the heat map from scratch
ggplot(barley, aes(x=year, y = variety, fill=yield) )+
geom_tile() +
facet_wrap(~ site, ncol=1) +
scale_fill_gradientn(colors = myColors)

Heat Maps Alternatives (1) There are several alternatives to heat maps. The best choice really depends on the data and the story you want to tell with this data. If there is a time component, the most obvious choice is a line plot like what we see in the viewer. Can you come up with the correct commands to create a similar looking plot?

The barley dataset is already available in the workspace. Feel free to check out its structure before you start!

# The heat map we want to replace
# Don't remove, it's here to help you!
myColors <- brewer.pal(9, "Reds")
ggplot(barley, aes(x = year, y = variety, fill = yield)) +
  geom_tile() +
  facet_wrap( ~ site, ncol = 1) +
  scale_fill_gradientn(colors = myColors)

# Line plots
ggplot(barley, aes(x=year, y = yield, col = variety, group = variety)) + geom_line() +
facet_wrap( ~ site, nrow = 1)

Heat Maps Alternatives (2) In the videos we saw two methods for depicting overlapping measurements of spread. You can use dodged error bars or you can use overlapping transparent ribbons (shown in the viewer). In this exercise we’ll try to recreate the second option, the transparent ribbons.

The barley dataset is available. You can use str(barley) to refresh its structure before heading over to the instructions.

# Create overlapping ribbon plot from scratch
ggplot(barley, aes(x=year, y=yield, col = site, group = site, fill= site) )+
stat_summary(fun.y = mean, geom = "line") +
stat_summary(fun.data = mean_sdl, fun.args= list(mult = 1), geom = "ribbon", col= NA, alpha=0.1)

LS0tCnRpdGxlOiAiRGF0YSBWaXN1YWxpemF0aW9uIHVzaW5nIGdncGxvdDIgcGFydCAyIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgojIyBTbW9vdGhpbmcKV2VsY29tZSB0byB0aGUgZXhlcmNpc2VzIGZvciB0aGUgc2Vjb25kIGdncGxvdDIgY291cnNlIQoKVG8gcHJhY3RpY2Ugb24gdGhlIHJlbWFpbmluZyBmb3VyIGxheWVycyAoc3RhdGlzdGljcywgY29vcmRpbmF0ZXMsIGZhY2V0cyBhbmQgdGhlbWVzKSwgd2UnbGwgY29udGludWUgd29ya2luZyBvbiBzZXZlcmFsIGRhdGFzZXRzIHRoYXQgd2UgYWxyZWFkeSBlbmNvdW50ZXJlZCBpbiB0aGUgZmlyc3QgY291cnNlLgoKVGhlIG10Y2FycyBkYXRhc2V0IGNvbnRhaW5zIGluZm9ybWF0aW9uIGZvciAzMiBjYXJzIGZyb20gTW90b3IgVHJlbmRzIG1hZ2F6aW5lIGZyb20gMTk3My4gVGhpcyBkYXRhc2V0IGlzIHNtYWxsLCBpbnR1aXRpdmUsIGFuZCBjb250YWlucyBhIHZhcmlldHkgb2YgY29udGludW91cyBhbmQgY2F0ZWdvcmljYWwgKGJvdGggbm9taW5hbCBhbmQgb3JkaW5hbCkgdmFyaWFibGVzLgoKSW4gdGhlIHByZXZpb3VzIGNvdXJzZSB3ZSBsZWFybmVkIGhvdyB0byBlZmZlY3RpdmVseSB1c2Ugc29tZSBiYXNpYyBnZW9tZXRyaWVzLCBzdWNoIGFzIHBvaW50LCBiYXIgYW5kIGxpbmUuIEluIHRoZSBmaXJzdCBjaGFwdGVyIG9mIHRoaXMgY291cnNlIHdlJ2xsIGV4cGxvcmUgc3RhdGlzdGljcyBhc3NvY2lhdGVkIHdpdGggc3BlY2lmaWMgZ2VvbXMsIGZvciBleGFtcGxlLCBzbW9vdGhpbmcgYW5kIGxpbmVzLgoKYGBge3J9CmxpYnJhcnkoZ2dwbG90MikKIyBFeHBsb3JlIHRoZSBtdGNhcnMgZGF0YSBmcmFtZSB3aXRoIHN0cigpCnN0cihtdGNhcnMpCgojIEEgc2NhdHRlciBwbG90IHdpdGggTE9FU1Mgc21vb3RoOgpnZ3Bsb3QobXRjYXJzLCBhZXMoeCA9IHd0LCB5ID0gbXBnKSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9zbW9vdGgoKQoKCiMgQSBzY2F0dGVyIHBsb3Qgd2l0aCBhbiBvcmRpbmFyeSBMZWFzdCBTcXVhcmVzIGxpbmVhciBtb2RlbDoKZ2dwbG90KG10Y2FycywgYWVzKHggPSB3dCwgeSA9IG1wZykpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21fc21vb3RoKG1ldGhvZD0ibG0iKQoKCiMgVGhlIHByZXZpb3VzIHBsb3QsIHdpdGhvdXQgQ0kgcmliYm9uOgpnZ3Bsb3QobXRjYXJzLCBhZXMoeCA9IHd0LCB5ID0gbXBnKSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgc2UgPSBGQUxTRSkKCgojIFRoZSBwcmV2aW91cyBwbG90LCB3aXRob3V0IHBvaW50czoKZ2dwbG90KG10Y2FycywgYWVzKHggPSB3dCwgeSA9IG1wZykpICsKICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBzZT1GQUxTRSkKCmBgYAoKR3JvdXBpbmcgdmFyaWFibGVzCldlJ2xsIGNvbnRpbnVlIHdpdGggdGhlIHByZXZpb3VzIGV4ZXJjaXNlIGJ5IGNvbnNpZGVyaW5nIHRoZSBzaXR1YXRpb24gb2YgbG9va2luZyBhdCBzdWItZ3JvdXBzIGluIG91ciBkYXRhc2V0LiBGb3IgdGhpcyB3ZSdsbCBlbmNvdW50ZXIgdGhlIGludmlzaWJsZSBncm91cCBhZXN0aGV0aWMuCgpgYGB7cn0KIyBnZ3Bsb3QyIGlzIGFscmVhZHkgbG9hZGVkCgojIERlZmluZSBjeWwgYXMgYSBmYWN0b3IgdmFyaWFibGUKZ2dwbG90KG10Y2FycywgYWVzKHggPSB3dCwgeSA9IG1wZywgY29sID0gZmFjdG9yKGN5bCkpKSArCiAgZ2VvbV9wb2ludCgpICsKICBzdGF0X3Ntb290aChtZXRob2QgPSAibG0iLCBzZSA9IEYpCgojIENvbXBsZXRlIHRoZSBmb2xsb3dpbmcgZ2dwbG90IGNvbW1hbmQgYXMgaW5zdHJ1Y3RlZApnZ3Bsb3QobXRjYXJzLCBhZXMoeCA9IHd0LCB5ID0gbXBnLCBjb2wgPSBmYWN0b3IoY3lsKSkpICsKICBnZW9tX3BvaW50KCkgKwogIHN0YXRfc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlID0gRikgKwogIHN0YXRfc21vb3RoKGFlcyhncm91cCA9MSApLCBtZXRob2QgPSAibG0iLCBzZSA9IEYpCgpgYGAKCk1vZGlmeWluZyBzdGF0X3Ntb290aApJbiB0aGUgcHJldmlvdXMgZXhlcmNpc2Ugd2Ugc2F3IGhvdyB0byBtb2RpZnkgdGhlIHNtb290aCBhZXN0aGV0aWMgYnkgcmVtb3ZpbmcgdGhlIDk1JSBDb25maWRlbmNlIEludGVydmFsLiBIZXJlIHdlJ2xsIGNvbnNpZGVyIHRoZSBzcGFuIGZvciBMT0VTUyBzbW9vdGhpbmcgYW5kIHdlJ2xsIHRha2UgYSBsb29rIGF0IGEgbmljZSBzY2VuYXJpbyBvZiBob3cgdG8gcHJvcGVybHkgbWFwIG91ciBkaWZmZXJlbnQgbW9kZWxzLgoKZ2dwbG90MiBpcyBhbHJlYWR5IGxvYWRlZCBhbmQgc2V2ZXJhbCBvZiB0aGUgbGluZWFyIG1vZGVscyB3ZSBsb29rZWQgYXQgaW4gdGhlIHR3byBwcmV2aW91cyBleGVyY2lzZXMgYXJlIGFscmVhZHkgZ2l2ZW4uCgpgYGB7cn0KIyBQbG90IDE6IGNoYW5nZSB0aGUgTE9FU1Mgc3BhbgpnZ3Bsb3QobXRjYXJzLCBhZXMoeCA9IHd0LCB5ID0gbXBnKSkgKwogIGdlb21fcG9pbnQoKSArCiAgIyBBZGQgc3BhbiBiZWxvdwogIGdlb21fc21vb3RoKHNlID0gRiwgc3Bhbj0wLjcpCgojIFBsb3QgMjogU2V0IHRoZSBvdmVyYWxsIG1vZGVsIHRvIExPRVNTIGFuZCB1c2UgYSBzcGFuIG9mIDAuNwpnZ3Bsb3QobXRjYXJzLCBhZXMoeCA9IHd0LCB5ID0gbXBnLCBjb2wgPSBmYWN0b3IoY3lsKSkpICsKICBnZW9tX3BvaW50KCkgKwogIHN0YXRfc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlID0gRikgKwogICMgQ2hhbmdlIG1ldGhvZCBhbmQgYWRkIHNwYW4gYmVsb3cKICBzdGF0X3Ntb290aChtZXRob2QgPSAibG9lc3MiLCBhZXMoZ3JvdXAgPSAxKSwKICAgICAgICAgICAgICBzZSA9IEYsIGNvbCA9ICJibGFjayIsIHNwYW49MC43KQoKIyBQbG90IDM6IFNldCBjb2wgdG8gIkFsbCIsIGluc2lkZSB0aGUgYWVzIGxheWVyIG9mIHN0YXRfc21vb3RoKCkKZ2dwbG90KG10Y2FycywgYWVzKHggPSB3dCwgeSA9IG1wZywgY29sID0gZmFjdG9yKGN5bCkpKSArCiAgZ2VvbV9wb2ludCgpICsKICBzdGF0X3Ntb290aChtZXRob2QgPSAibG0iLCBzZSA9IEYpICsKICBzdGF0X3Ntb290aChtZXRob2QgPSAibG9lc3MiLAogICAgICAgICAgICAgICMgQWRkIGNvbCBpbnNpZGUgYWVzKCkKICAgICAgICAgICAgICBhZXMoZ3JvdXAgPSAxLGNvbCA9ICJBbGwiKSwKICAgICAgICAgICAgICAjIFJlbW92ZSB0aGUgY29sIGFyZ3VtZW50IGJlbG93CiAgICAgICAgICAgICAgc2UgPSBGLCBzcGFuID0gMC43KQoKIyBQbG90IDQ6IEFkZCBzY2FsZV9jb2xvcl9tYW51YWwgdG8gY2hhbmdlIHRoZSBjb2xvcnMKCm15Q29sb3JzIDwtIGMoIiMxQjlFNzciLCAiI0Q5NUYwMiIsICIjNzU3MEIzIiwgImJsYWNrIikKZ2dwbG90KG10Y2FycywgYWVzKHggPSB3dCwgeSA9IG1wZywgY29sID0gZmFjdG9yKGN5bCkpKSArCiAgZ2VvbV9wb2ludCgpICsKICBzdGF0X3Ntb290aChtZXRob2QgPSAibG0iLCBzZSA9IEYsIHNwYW4gPSAwLjc1KSArCiAgc3RhdF9zbW9vdGgobWV0aG9kID0gImxvZXNzIiwKICAgICAgICAgICAgICBhZXMoZ3JvdXAgPSAxLCBjb2w9IkFsbCIpLAogICAgICAgICAgICAgIHNlID0gRiwgc3BhbiA9IDAuNykgKwogICMgQWRkIGNvcnJlY3QgYXJndW1lbnRzIHRvIHNjYWxlX2NvbG9yX21hbnVhbAogIHNjYWxlX2NvbG9yX21hbnVhbCgiQ3lsaW5kZXJzIiwgdmFsdWVzPW15Q29sb3JzKQpgYGAKCk1vZGlmeWluZyBzdGF0X3Ntb290aCAoMikKSW4gdGhpcyBleGVyY2lzZSB3ZSdsbCB0YWtlIGEgbG9vayBhdCBhIG1vcmUgc3VidGxlIGV4YW1wbGUgb2YgZGVmaW5pbmcgYW5kIHVzaW5nIGxpbmVhciBtb2RlbHMuIGdncGxvdDIgYW5kIHRoZSBWb2NhYiBkYXRhIGZyYW1lIGFyZSBhbHJlYWR5IGxvYWRlZCBmb3IgeW91LgoKYGBge3J9CmxpYnJhcnkoY2FyKQpsaWJyYXJ5KFJDb2xvckJyZXdlcikKZGF0YShWb2NhYikKIyBQbG90IDE6IEppdHRlcmVkIHNjYXR0ZXIgcGxvdCwgYWRkIGEgbGluZWFyIG1vZGVsIChsbSkgc21vb3RoOgpnZ3Bsb3QoVm9jYWIsIGFlcyh4ID0gZWR1Y2F0aW9uLCB5ID0gdm9jYWJ1bGFyeSkpICsKICBnZW9tX2ppdHRlcihhbHBoYSA9IDAuMikgKwogIHN0YXRfc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlPUZBTFNFKQoKCiMgUGxvdCAyOiBPbmx5IGxtLCBjb2xvcmVkIGJ5IHllYXIKZ2dwbG90KFZvY2FiLCBhZXMoeCA9IGVkdWNhdGlvbiwgeSA9IHZvY2FidWxhcnksIGNvbD1mYWN0b3IoeWVhcikpKSArCiAgc3RhdF9zbW9vdGgobWV0aG9kID0gImxtIiwgc2U9RkFMU0UpCgojIFBsb3QgMzogU2V0IGEgY29sb3IgYnJld2VyIHBhbGV0dGUKZ2dwbG90KFZvY2FiLCBhZXMoeCA9IGVkdWNhdGlvbiwgeSA9IHZvY2FidWxhcnksIGNvbD1mYWN0b3IoeWVhcikpKSArCiAgc3RhdF9zbW9vdGgobWV0aG9kID0gImxtIiwgc2U9RkFMU0UpICsKICBzY2FsZV9jb2xvcl9icmV3ZXIoIkJsdWVzIikKCiMgUGxvdCA0OiBBZGQgdGhlIGdyb3VwLCBzcGVjaWZ5IGFscGhhIGFuZCBzaXplCmdncGxvdChWb2NhYiwgYWVzKHggPSBlZHVjYXRpb24sIHkgPSB2b2NhYnVsYXJ5LCBjb2wgPSB5ZWFyLCBncm91cD1mYWN0b3IoeWVhcikpKSArCiAgc3RhdF9zbW9vdGgobWV0aG9kID0gImxtIiwgc2UgPSBGLCBhbHBoYT0wLjYsIHNpemU9MikgKwogIHNjYWxlX2NvbG9yX2dyYWRpZW50bihjb2xvcnMgPSBicmV3ZXIucGFsKDksIllsT3JSZCIpKQpgYGAKClF1YW50aWxlcwpUaGUgcHJldmlvdXMgZXhhbXBsZSB1c2VkIHRoZSBWb2NhYiBkYXRhc2V0IGFuZCBhcHBsaWVkIGxpbmVhciBtb2RlbHMgZGVzY3JpYmluZyB2b2NhYnVsYXJ5IGJ5IGVkdWNhdGlvbiBmb3IgZGlmZmVyZW50IHllYXJzLiBIZXJlIHdlJ2xsIGNvbnRpbnVlIHdpdGggdGhhdCBleGFtcGxlIGJ5IHVzaW5nIHN0YXRfcXVhbnRpbGUoKSB0byBhcHBseSBhIHF1YW50aWxlIHJlZ3Jlc3Npb24gKG1ldGhvZCBycSkuCgpCeSBkZWZhdWx0LCB0aGUgMXN0LCAybmQgKGkuZS4gbWVkaWFuKSwgYW5kIDNyZCBxdWFydGlsZXMgYXJlIG1vZGVsZWQgYXMgYSByZXNwb25zZSB0byB0aGUgcHJlZGljdG9yIHZhcmlhYmxlLCBpbiB0aGlzIGNhc2UgZWR1Y2F0aW9uLiBTcGVjaWZpYyBxdWFudGlsZXMgY2FuIGJlIHNwZWNpZmllZCB3aXRoIHRoZSBxdWFudGlsZXMgYXJndW1lbnQuCgpJZiB5b3Ugd2FudCB0byBzcGVjaWZ5IG1hbnkgcXVhbnRpbGUgYW5kIGNvbG9yIGFjY29yZGluZyB0byB5ZWFyLCB0aGVuIHRoaW5ncyBnZXQgdG9vIGJ1c3kuIFdlJ2xsIGV4cGxvcmUgd2F5cyBvZiBkZWFsaW5nIHdpdGggdGhpcyBpbiB0aGUgbmV4dCBjaGFwdGVyLgoKYGBge3J9CmxpYnJhcnkoU3BhcnNlTSkKIyBVc2Ugc3RhdF9xdWFudGlsZSBpbnN0ZWFkIG9mIHN0YXRfc21vb3RoOgpnZ3Bsb3QoVm9jYWIsIGFlcyh4ID0gZWR1Y2F0aW9uLCB5ID0gdm9jYWJ1bGFyeSwgY29sID0geWVhciwgZ3JvdXAgPSBmYWN0b3IoeWVhcikpKSArCiAgc3RhdF9xdWFudGlsZShhbHBoYSA9IDAuNiwgc2l6ZSA9IDIpICsKICBzY2FsZV9jb2xvcl9ncmFkaWVudG4oY29sb3JzID0gYnJld2VyLnBhbCg5LCJZbE9yUmQiKSkKCiMgU2V0IHF1YW50aWxlIHRvIDAuNToKZ2dwbG90KFZvY2FiLCBhZXMoeCA9IGVkdWNhdGlvbiwgeSA9IHZvY2FidWxhcnksIGNvbCA9IHllYXIsIGdyb3VwID0gZmFjdG9yKHllYXIpKSkgKwogIHN0YXRfcXVhbnRpbGUoYWxwaGEgPSAwLjYsIHNpemUgPSAyLCBxdWFudGlsZXMgPSAwLjUpICsKICBzY2FsZV9jb2xvcl9ncmFkaWVudG4oY29sb3JzID0gYnJld2VyLnBhbCg5LCJZbE9yUmQiKSkKCgpgYGAKClN1bQpBbm90aGVyIHVzZWZ1bCBzdGF0IGZ1bmN0aW9uIGlzIHN0YXRfc3VtKCkgd2hpY2ggY2FsY3VsYXRlcyB0aGUgY291bnQgZm9yIGVhY2ggZ3JvdXAuCgpgYGB7cn0KIyBQbG90IHdpdGggbGluZWFyIGFuZCBsb2VzcyBtb2RlbApwIDwtIGdncGxvdChWb2NhYiwgYWVzKHggPSBlZHVjYXRpb24sIHkgPSB2b2NhYnVsYXJ5KSkgKwogICAgICAgc3RhdF9zbW9vdGgobWV0aG9kID0gImxvZXNzIiwgYWVzKGNvbCA9ICJ4IiksIHNlID0gRikgKwogICAgICAgc3RhdF9zbW9vdGgobWV0aG9kID0gImxtIiwgYWVzKGNvbCA9ICJ5IiksIHNlID0gRikgKwogICAgICAgc2NhbGVfY29sb3JfZGlzY3JldGUoIk1vZGVsIiwgbGFiZWxzID0gYygieCIgPSAiTE9FU1MiLCAieSIgPSAibG0iKSkKCiMgQWRkIHN0YXRfc3VtCnAgKyBzdGF0X3N1bSgpCgojIEFkZCBzdGF0X3N1bSBhbmQgc2V0IHNpemUgcmFuZ2UKcCArIHN0YXRfc3VtKCkgKyBzY2FsZV9zaXplKHJhbmdlID0gYygxLDEwKSkKYGBgCgpQcmVwYXJhdGlvbnMKSGVyZSB3ZSdsbCBsb29rIGF0IHN0YXRfc3VtbWFyeSgpIGluIGFjdGlvbi4gV2UnbGwgYnVpbGQgdXAgdmFyaW91cyBwbG90cyBvbmUtYnktb25lLgoKSW4gdGhpcyBleGVyY2lzZSB3ZSdsbCBjb25zaWRlciB0aGUgcHJlcGFyYXRpb25zLiBUaGF0IG1lYW5zIHdlJ2xsIG1ha2Ugc3VyZSB0aGUgZGF0YSBpcyBpbiB0aGUgcmlnaHQgZm9ybWF0IGFuZCB0aGF0IGFsbCB0aGUgcG9zaXRpb25zIHRoYXQgd2UgbWlnaHQgdXNlIGluIG91ciBwbG90cyBhcmUgZGVmaW5lZC4gTGFzdGx5LCB3ZSdsbCBzZXQgdGhlIGJhc2UgbGF5ZXIgZm9yIG91ciBwbG90LiBnZ3Bsb3QyIGlzIGFscmVhZHkgbG9hZGVkLCBzbyB5b3UgY2FuIGdldCBzdGFydGVkIHN0cmFpZ2h0IGF3YXkhCgpgYGB7cn0KIyBEaXNwbGF5IHN0cnVjdHVyZSBvZiBtdGNhcnMKc3RyKG10Y2FycykKCiMgQ29udmVydCBjeWwgYW5kIGFtIHRvIGZhY3RvcnM6Cm10Y2FycyRjeWwgPC0gYXMuZmFjdG9yKG10Y2FycyRjeWwpCm10Y2FycyRhbSA8LSBhcy5mYWN0b3IobXRjYXJzJGFtKQoKIyBEZWZpbmUgcG9zaXRpb25zOgpwb3NuLmQgPC0gcG9zaXRpb25fZG9kZ2Uod2lkdGggPSAwLjEpCnBvc24uamQgPC0gcG9zaXRpb25faml0dGVyZG9kZ2Uoaml0dGVyLndpZHRoID0gMC4xLCBkb2RnZS53aWR0aCA9IDAuMikKcG9zbi5qIDwtIHBvc2l0aW9uX2ppdHRlcih3aWR0aCA9IDAuMikKCiMgYmFzZSBsYXllcnM6Cnd0LmN5bC5hbSA8LSBnZ3Bsb3QobXRjYXJzLCBhZXMoeD1jeWwsIHk9d3QsIGNvbD1hbSwgZmlsbD0gYW0sIGdyb3VwPWFtKSkKYGBgCgpQbG90dGluZyB2YXJpYXRpb25zCk5vdyB0aGF0IHRoZSBwcmVwYXJhdGlvbiB3b3JrIGlzIGRvbmUsIGxldCdzIGhhdmUgYSBsb29rIGF0IGF0IHN0YXRfc3VtbWFyeSgpLgoKZ2dwbG90MiBpcyBhbHJlYWR5IGxvYWRlZCwgYXMgaXMgd3QuY3lsLmFtLCB3aGljaCBpcyBkZWZpbmVkIGFzCgp3dC5jeWwuYW0gPC0gZ2dwbG90KG10Y2FycywgYWVzKHggPSBjeWwsICB5ID0gd3QsIGNvbCA9IGFtLCBmaWxsID0gYW0sIGdyb3VwID0gYW0pKQpBbHNvIGFsbCB0aGUgcG9zaXRpb24gb2JqZWN0cyBvZiB0aGUgcHJldmlvdXMgZXhlcmNpc2UsIHBvc24uZCwgcG9zbi5qZCBhbmQgcG9zbi5qLCBhcmUgYXZhaWxhYmxlLiBGb3Igc3RhcnRlcnMsIFBsb3QgMSBpcyBhbHJlYWR5IGNvZGVkIGZvciB5b3UuCgpgYGB7cn0KbGlicmFyeShIbWlzYykKIyB3dC5jeWwuYW0sIHBvc24uZCwgcG9zbi5qZCBhbmQgcG9zbi5qIGFyZSBhdmFpbGFibGUKCiMgUGxvdCAxOiBKaXR0ZXJlZCwgZG9kZ2VkIHNjYXR0ZXIgcGxvdCB3aXRoIHRyYW5zcGFyZW50IHBvaW50cwp3dC5jeWwuYW0gKwogIGdlb21fcG9pbnQocG9zaXRpb24gPSBwb3NuLmpkLCBhbHBoYSA9IDAuNikKCiMgUGxvdCAyOiBNZWFuIGFuZCBTRCAtIHRoZSBlYXN5IHdheQp3dC5jeWwuYW0gKwogIGdlb21fcG9pbnQocG9zaXRpb24gPSBwb3NuLmpkLCBhbHBoYSA9IDAuNikgKwogIHN0YXRfc3VtbWFyeShmdW4uZGF0YSA9IG1lYW5fc2RsLCBmdW4uYXJncyA9IGxpc3QobXVsdD0xKSwgcG9zaXRpb24gPSBwb3NuLmQpCgoKIyBQbG90IDM6IE1lYW4gYW5kIDk1JSBDSSAtIHRoZSBlYXN5IHdheQp3dC5jeWwuYW0gKwogIGdlb21fcG9pbnQocG9zaXRpb24gPSBwb3NuLmpkLCBhbHBoYSA9IDAuNikgKwogIHN0YXRfc3VtbWFyeShmdW4uZGF0YSA9IG1lYW5fY2xfbm9ybWFsLCBwb3NpdGlvbiA9IHBvc24uZCkKCgojIFBsb3QgNDogTWVhbiBhbmQgU0QgLSB3aXRoIFQtdGlwcGVkIGVycm9yIGJhcnMgLSBmaWxsIGluIF9fXwp3dC5jeWwuYW0gKwogIHN0YXRfc3VtbWFyeShnZW9tID0gInBvaW50IiwgZnVuLnkgPSBtZWFuLAogICAgICAgICAgICAgICBwb3NpdGlvbiA9IHBvc24uZCkgKwogIHN0YXRfc3VtbWFyeShnZW9tID0gImVycm9yYmFyIiwgZnVuLmRhdGEgPSBtZWFuX3NkbCwKICAgICAgICAgICAgICAgcG9zaXRpb24gPSBwb3NuLmQsIGZ1bi5hcmdzID0gbGlzdChtdWx0ID0gMSksIHdpZHRoID0gMC4xKQpgYGAKCkN1c3RvbSBGdW5jdGlvbnMKSW4gdGhlIHZpZGVvIHdlIHNhdyB0aGF0IHRoZSBvbmx5IGRpZmZlcmVuY2UgYmV0d2VlbiBnZ3Bsb3QyOjptZWFuX3NkbCgpIGFuZCBIbWlzYzo6c21lYW4uc2RsKCkgaXMgdGhlIG5hbWluZyBjb252ZW50aW9uLiBJbiBvcmRlciB0byB1c2UgdGhlIHJlc3VsdHMgb2YgYSBmdW5jdGlvbiBkaXJlY3RseSBpbiBnZ3Bsb3QyIHdlIG5lZWQgdG8gZW5zdXJlIHRoYXQgdGhlIG5hbWVzIG9mIHRoZSB2YXJpYWJsZXMgbWF0Y2ggdGhlIGFlc3RoZXRpY3MgbmVlZGVkIGZvciBvdXIgcmVzcGVjdGl2ZSBnZW9tcy4KCkhlcmUgd2UnbGwgY3JlYXRlIHR3byBuZXcgZnVuY3Rpb25zIGluIG9yZGVyIHRvIGNyZWF0ZSB0aGUgcGxvdCBzaG93biBpbiB0aGUgdmlld2VyLiBPbmUgZnVuY3Rpb24gd2lsbCBtZWFzdXJlIHRoZSBmdWxsIHJhbmdlIG9mIHRoZSBkYXRhc2V0IGFuZCB0aGUgb3RoZXIgd2lsbCBtZWFzdXJlIHRoZSBpbnRlcnF1YXJ0aWxlIHJhbmdlLgoKQSBwbGF5IHZlY3RvciwgeHgsIGhhcyBiZWVuIGNyZWF0ZWQgZm9yIHlvdS4gRXhlY3V0ZQoKbWVhbl9zZGwoeHgsIG11bHQgPSAxKQppbiB0aGUgUiBDb25zb2xlIGFuZCBjb25zaWRlciB0aGUgZm9ybWF0IG9mIHRoZSBvdXRwdXQuIFlvdSdsbCBoYXZlIHRvIHByb2R1Y2UgZnVuY3Rpb25zIHdoaWNoIHJldHVybiBzaW1pbGFyIG91dHB1dHMuCgpgYGB7cn0KeHggPSBjKDE6MTAwKQojIFBsYXkgdmVjdG9yIHh4IGlzIGF2YWlsYWJsZQoKIyBGdW5jdGlvbiB0byBzYXZlIHJhbmdlIGZvciB1c2UgaW4gZ2dwbG90CmdnX3JhbmdlIDwtIGZ1bmN0aW9uKHgpIHsKICAjIENoYW5nZSB4IGJlbG93IHRvIHJldHVybiB0aGUgaW5zdHJ1Y3RlZCB2YWx1ZXMKICBkYXRhLmZyYW1lKHltaW4gPSBtaW4oeCksICMgTWluCiAgICAgICAgICAgICB5bWF4ID0gbWF4KHgpKSAjIE1heAp9CgpnZ19yYW5nZSh4eCkKIyBSZXF1aXJlZCBvdXRwdXQ6CiMgICB5bWluIHltYXgKIyAxICAgIDEgIDEwMAoKIyBGdW5jdGlvbiB0byBDdXN0b20gZnVuY3Rpb246Cm1lZF9JUVIgPC0gZnVuY3Rpb24oeCkgewogICMgQ2hhbmdlIHggYmVsb3cgdG8gcmV0dXJuIHRoZSBpbnN0cnVjdGVkIHZhbHVlcwogIGRhdGEuZnJhbWUoeSA9IG1lZGlhbih4KSwgIyBNZWRpYW4KICAgICAgICAgICAgIHltaW4gPSBxdWFudGlsZSh4KVsyXSwgIyAxc3QgcXVhcnRpbGUKICAgICAgICAgICAgIHltYXggPSBxdWFudGlsZSh4KVs0XSkgICMgM3JkIHF1YXJ0aWxlCn0KCm1lZF9JUVIoeHgpCiMgUmVxdWlyZWQgb3V0cHV0OgojICAgICAgICB5ICB5bWluICB5bWF4CiMgMjUlIDUwLjUgMjUuNzUgNzUuMjUKYGBgCgpDdXN0b20gRnVuY3Rpb25zICgyKQpJbiB0aGUgbGFzdCBleGVyY2lzZSB3ZSBjcmVhdGVkIGZ1bmN0aW9ucyB0aGF0IHdpbGwgYWxsb3cgdXMgdG8gcGxvdCB0aGUgc28tY2FsbGVkIGZpdmUtbnVtYmVyIHN1bW1hcnkgKHRoZSBtaW5pbXVtLCAxc3QgcXVhcnRpbGUsIG1lZGlhbiwgM3JkIHF1YXJ0aWxlLCBhbmQgdGhlIG1heGltdW0pLiBIZXJlLCB3ZSdsbCBpbXBsZW1lbnQgdGhhdCBpbnRvIGEgdW5pcXVlIHBsb3QgdHlwZS4KCkFsbCB0aGUgZnVuY3Rpb25zIGFuZCBvYmplY3RzIGZyb20gdGhlIHByZXZpb3VzIGV4ZXJjaXNlIGFyZSBhdmFpbGFibGUgaW5jbHVkaW5nIHRoZSB1cGRhdGVkIG10Y2FycyBkYXRhIGZyYW1lLCB0aGUgcG9zaXRpb24gb2JqZWN0IHBvc24uZCwgdGhlIGJhc2UgbGF5ZXJzIHd0LmN5bC5hbSBhbmQgdGhlIGZ1bmN0aW9ucyBtZWRfSVFSKCkgYW5kIGdnX3JhbmdlKCkuCgpUaGUgcGxvdCB5b3UnbGwgZW5kIHVwIHdpdGggYXQgdGhlIGVuZCBvZiB0aGlzIGV4ZXJjaXNlIGlzIHNob3duIG9uIHRoZSByaWdodC4KCmBgYHtyfQojIFRoZSBiYXNlIGdncGxvdCBjb21tYW5kLCB5b3UgZG9uJ3QgaGF2ZSB0byBjaGFuZ2UgdGhpcwp3dC5jeWwuYW0gPC0gZ2dwbG90KG10Y2FycywgYWVzKHggPSBjeWwseSA9IHd0LCBjb2wgPSBhbSwgZmlsbCA9IGFtLCBncm91cCA9IGFtKSkKCiMgQWRkIHRocmVlIHN0YXRfc3VtbWFyeSBjYWxscyB0byB3dC5jeWwuYW0Kd3QuY3lsLmFtICsKICBzdGF0X3N1bW1hcnkoZ2VvbSA9ICJsaW5lcmFuZ2UiLCBmdW4uZGF0YSA9IG1lZF9JUVIsCiAgICAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zbi5kLCBzaXplID0gMykgKwogIHN0YXRfc3VtbWFyeShnZW9tID0gImxpbmVyYW5nZSIsIGZ1bi5kYXRhID0gZ2dfcmFuZ2UsCiAgICAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zbi5kLCBzaXplID0gMywKICAgICAgICAgICAgICAgYWxwaGEgPSAwLjQpICsKICBzdGF0X3N1bW1hcnkoZ2VvbSA9ICJwb2ludCIsIGZ1bi55ID0gbWVkaWFuLAogICAgICAgICAgICAgICBwb3NpdGlvbiA9IHBvc24uZCwgc2l6ZSA9IDMsCiAgICAgICAgICAgICAgIGNvbCA9ICJibGFjayIsIHNoYXBlID0gIlgiKQpgYGAKClpvb21pbmcgSW4KSW4gdGhlIHZpZGVvLCB5b3Ugc2F3IGRpZmZlcmVudCB3YXlzIG9mIHVzaW5nIHRoZSBjb29yZGluYXRlcyBsYXllciB0byB6b29tIGluLiBJbiB0aGlzIGV4ZXJjaXNlLCB3ZSdsbCBjb21wYXJlIHNvbWUgb2YgdGhlIHRlY2huaXF1ZXMgYWdhaW4uCgpBcyB1c3VhbCwgeW91J2xsIGJlIHdvcmtpbmcgd2l0aCB0aGUgbXRjYXJzIGRhdGFzZXQsIHdoaWNoIGlzIGFscmVhZHkgY2xlYW5lZCB1cCBmb3IgeW91IChjeWwgYW5kIGFtIGFyZSBjYXRlZ29yaWNhbCB2YXJpYWJsZXMpLiBBbHNvIHAsIGEgZ2dwbG90IG9iamVjdCB5b3UgY29kZWQgaW4gdGhlIHByZXZpb3VzIGNoYXB0ZXIsIGlzIGFscmVhZHkgYXZhaWxhYmxlLiBFeGVjdXRlIHAgaW4gdGhlIGNvbnNvbGUgdG8gY2hlY2sgaXQgb3V0LgoKYGBge3J9CiMgQmFzaWMgZ2dwbG90KCkgY29tbWFuZCwgY29kZWQgZm9yIHlvdQpwIDwtIGdncGxvdChtdGNhcnMsIGFlcyh4ID0gd3QsIHkgPSBocCwgY29sID0gYW0pKSArIGdlb21fcG9pbnQoKSArIGdlb21fc21vb3RoKCkKCiMgQWRkIHNjYWxlX3hfY29udGludW91cwpwICsgc2NhbGVfeF9jb250aW51b3VzKGxpbWl0cyA9IGMoMyw2KSwgZXhwYW5kPWMoMCwwKSkKCiMgVGhlIHByb3BlciB3YXkgdG8gem9vbSBpbjoKcCArIGNvb3JkX2NhcnRlc2lhbih4bGltID0gYygzLDYpKQoKYGBgCgpBc3BlY3QgUmF0aW8KV2UgY2FuIHNldCB0aGUgYXNwZWN0IHJhdGlvIG9mIGEgcGxvdCB3aXRoIGNvb3JkX2ZpeGVkKCkgb3IgY29vcmRfZXF1YWwoKS4gQm90aCB1c2UgYXNwZWN0ID0gMSBhcyBhIGRlZmF1bHQuIEEgMToxIGFzcGVjdCByYXRpbyBpcyBtb3N0IGFwcHJvcHJpYXRlIHdoZW4gdHdvIGNvbnRpbnVvdXMgdmFyaWFibGVzIGFyZSBvbiB0aGUgc2FtZSBzY2FsZSwgYXMgd2l0aCB0aGUgaXJpcyBkYXRhc2V0LgoKQWxsIHZhcmlhYmxlcyBhcmUgbWVhc3VyZWQgaW4gY2VudGltZXRlcnMsIHNvIGl0IG9ubHkgbWFrZXMgc2Vuc2UgdGhhdCBvbmUgdW5pdCBvbiB0aGUgcGxvdCBzaG91bGQgYmUgdGhlIHNhbWUgcGh5c2ljYWwgZGlzdGFuY2Ugb24gZWFjaCBheGlzLiBUaGlzIGdpdmVzIGEgbW9yZSB0cnV0aGZ1bCBkZXBpY3Rpb24gb2YgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZSB0d28gdmFyaWFibGVzIHNpbmNlIHRoZSBhc3BlY3QgcmF0aW8gY2FuIGNoYW5nZSB0aGUgYW5nbGUgb2Ygb3VyIHNtb290aGluZyBsaW5lLiBUaGlzIHdvdWxkIGdpdmUgYW4gZXJyb25lb3VzIGltcHJlc3Npb24gb2YgdGhlIGRhdGEuCgpPZiBjb3Vyc2UgdGhlIHVuZGVybHlpbmcgbGluZWFyIG1vZGVscyBkb24ndCBjaGFuZ2UsIGJ1dCBvdXIgcGVyY2VwdGlvbiBjYW4gYmUgaW5mbHVlbmNlZCBieSB0aGUgYW5nbGUgZHJhd24uCgpgYGB7cn0KIyBDb21wbGV0ZSBiYXNpYyBzY2F0dGVyIHBsb3QgZnVuY3Rpb24KYmFzZS5wbG90IDwtIGdncGxvdChpcmlzLCBhZXMoeCA9IFNlcGFsLkxlbmd0aCwgeT1TZXBhbC5XaWR0aCwgY29sPVNwZWNpZXMpKSArCiAgICAgICAgICAgICAgIGdlb21faml0dGVyKCkgKwogICAgICAgICAgICAgICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBzZSA9IEYpCgojIFBsb3QgYmFzZS5wbG90OiBkZWZhdWx0IGFzcGVjdCByYXRpbwpiYXNlLnBsb3QKCiMgRml4IGFzcGVjdCByYXRpbyAoMToxKSBvZiBiYXNlLnBsb3QKYmFzZS5wbG90ICsKY29vcmRfZXF1YWwoKQpgYGAKClBpZSBDaGFydHMKVGhlIGNvb3JkX3BvbGFyKCkgZnVuY3Rpb24gY29udmVydHMgYSBwbGFuYXIgeC15IGNhcnRlc2lhbiBwbG90IHRvIHBvbGFyIGNvb3JkaW5hdGVzLiBUaGlzIGNhbiBiZSB1c2VmdWwgaWYgeW91IGFyZSBwcm9kdWNpbmcgcGllIGNoYXJ0cy4KCldlIGNhbiBpbWFnaW5lIHR3byBmb3JtcyBmb3IgcGllIGNoYXJ0cyAtIHRoZSB0eXBpY2FsIGZpbGxlZCBjaXJjbGUsIG9yIGEgY29sb3JlZCByaW5nLgoKQXMgYW4gZXhhbXBsZSwgY29uc2lkZXIgdGhlIHN0YWNrZWQgYmFyIGNoYXJ0IHNob3duIGluIHRoZSB2aWV3ZXIuIEltYWdpbmUgdGhhdCB3ZSBqdXN0IHRha2UgdGhlIHkgYXhpcyBvbiB0aGUgbGVmdCBhbmQgYmVuZCBpdCB1bnRpbCBpdCBsb29wcyBiYWNrIG9uIGl0c2VsZiwgd2hpbGUgZXhwYW5kaW5nIHRoZSByaWdodCBzaWRlIGFzIHdlIGdvIGFsb25nLiBXZSdkIGVuZCB1cCB3aXRoIGEgcGllIGNoYXJ0IC0gaXQncyBzaW1wbHkgYSBiYXIgY2hhcnQgdHJhbnNmb3JtZWQgb250byBhIHBvbGFyIGNvb3JkaW5hdGUgc3lzdGVtLgoKVHlwaWNhbCBwaWUgY2hhcnRzIG9taXQgYWxsIG9mIHRoZSBub24tZGF0YSBpbmssIHdoaWNoIHdlJ2xsIGxlYXJuIGFib3V0IGluIHRoZSBuZXh0IGNoYXB0ZXIuIFBpZSBjaGFydHMgYXJlIG5vdCByZWFsbHkgYmV0dGVyIHRoYW4gc3RhY2tlZCBiYXIgY2hhcnRzLCBidXQgd2UnbGwgY29tZSBiYWNrIHRvIHRoaXMgcG9pbnQgaW4gdGhlIGZvdXJ0aCBjaGFwdGVyIG9uIGJlc3QgcHJhY3RpY2VzLgoKVGhlIG10Y2FycyBkYXRhIGZyYW1lIGlzIGF2YWlsYWJsZSwgd2l0aCBjeWwgY29udmVydGVkIHRvIGEgZmFjdG9yIGZvciB5b3UuCgpgYGB7cn0KIyBDcmVhdGUgc3RhY2tlZCBiYXIgcGxvdDogdGhpbi5iYXIKdGhpbi5iYXIgPC0gZ2dwbG90KG10Y2FycywgYWVzKHg9MSwgZmlsbCA9IGN5bCkpICsKICAgICAgICAgICAgICBnZW9tX2JhcigpCgojIENvbnZlcnQgdGhpbi5iYXIgdG8gcGllIGNoYXJ0CnRoaW4uYmFyICsgY29vcmRfcG9sYXIodGhldGEgPSAieSIpCgojIENyZWF0ZSBzdGFja2VkIGJhciBwbG90OiB3aWRlLmJhcgp3aWRlLmJhciA8LSBnZ3Bsb3QobXRjYXJzLCBhZXMoeD0xLCBmaWxsID0gY3lsKSkgKwogICAgICAgICAgICAgIGdlb21fYmFyKHdpZHRoID0gMSkKCgojIENvbnZlcnQgd2lkZS5iYXIgdG8gcGllIGNoYXJ0CndpZGUuYmFyICsgY29vcmRfcG9sYXIodGhldGEgPSAieSIpCmBgYAoKRmFjZXRzOiB0aGUgYmFzaWNzClRoZSBtb3N0IHN0cmFpZ2h0Zm9yd2FyZCB3YXkgb2YgdXNpbmcgZmFjZXRzIGlzIGZhY2V0X2dyaWQoKS4gSGVyZSB3ZSBqdXN0IG5lZWQgdG8gc3BlY2lmeSB0aGUgY2F0ZWdvcmljYWwgdmFyaWFibGUgdG8gdXNlIG9uIHJvd3MgYW5kIGNvbHVtbnMgdXNpbmcgZm9ybXVsYSBub3RhdGlvbi4KCk5vdGljZSB0aGF0IHdlIGNhbiBhbHNvIHRha2UgYWR2YW50YWdlIG9mIG9yZGluYWwgdmFyaWFibGVzIGJ5IHBvc2l0aW9uaW5nIHRoZW0gaW4gdGhlIGNvcnJlY3Qgb3JkZXIgYXMgY29sdW1ucyBvciByb3dzLCBhcyBpcyB0aGUgY2FzZSB3aXRoIHRoZSBudW1iZXIgb2YgY3lsaW5kZXJzLiBHZXQgc29tZSBoYW5kcy1vbiBwcmFjdGljZSBpbiB0aGlzIGV4ZXJjaXNlOyBnZ3Bsb3QyIGlzIGFscmVhZHkgbG9hZGVkIGZvciB5b3UuIG10Y2FycyBpcyBhdmFpbGFibGUgd2hlcmUgY3lsIGFuZCBhbSBhcmUgZmFjdG9ycy4KCmBgYHtyfQojIEJhc2ljIHNjYXR0ZXIgcGxvdDoKcCA8LSBnZ3Bsb3QobXRjYXJzLCBhZXMoeCA9IHd0LCB5ID0gbXBnKSkgKwogIGdlb21fcG9pbnQoKQoKIyBTZXBhcmF0ZSByb3dzIGFjY29yZGluZyB0byB0cmFuc21pc3Npb24gdHlwZSwgYW0KcCArIGZhY2V0X2dyaWQoYW0gfiAuKQoKIyBTZXBhcmF0ZSBjb2x1bW5zIGFjY29yZGluZyB0byBjeWxpbmRlcnMsIGN5bApwICsgZmFjZXRfZ3JpZCguIH4gY3lsKQoKIyBTZXBhcmF0ZSBieSBib3RoIGNvbHVtbnMgYW5kIHJvd3MgCnAgKyBmYWNldF9ncmlkKGFtIH4gY3lsKQoKYGBgCgpNYW55IHZhcmlhYmxlcwpGYWNldHMgYXJlIGFub3RoZXIgd2F5IG9mIHByZXNlbnRpbmcgY2F0ZWdvcmljYWwgdmFyaWFibGVzLiBSZWNhbGwgdGhhdCB3ZSBzYXcgYWxsIHRoZSB3YXlzIG9mIGNvbWJpbmluZyB2YXJpYWJsZXMsIGJvdGggY2F0ZWdvcmljYWwgYW5kIGNvbnRpbnVvdXMsIGluIHRoZSBhZXN0aGV0aWNzIGNoYXB0ZXIuIFNvbWV0aW1lcyBpdCdzIHBvc3NpYmxlIHRvIG92ZXJkbyBpdC4gSGVyZSB3ZSdsbCBwcmVzZW50IGEgcGxvdCB3aXRoIDYgdmFyaWFibGVzIGFuZCBzZWUgaWYgd2UgY2FuIGFkZCBldmVuIG1vcmUuCgpMZXQncyBiZWdpbiBieSB1c2luZyBhIHRyaWNrIHRvIG1hcCB0d28gdmFyaWFibGVzIG9udG8gdHdvIGNvbG9yIHNjYWxlcyAtIGh1ZSBhbmQgbGlnaHRuZXNzLiBXZSBjb21iaW5lIGN5bCBhbmQgYW0gaW50byBhIHNpbmdsZSB2YXJpYWJsZSBjeWxfYW0uIFRvIGFjY29tbW9kYXRlIHRoaXMgd2UgYWxzbyBtYWtlIGEgbmV3IGNvbG9yIHBhbGV0dGUgd2l0aCBhbHRlcm5hdGluZyByZWQgYW5kIGJsdWUgb2YgaW5jcmVhc2luZyBkYXJrbmVzcy4gVGhpcyBpcyBzYXZlZCBhcyBteUNvbAoKYGBge3J9CiMgQ29kZSB0byBjcmVhdGUgdGhlIGN5bF9hbSBjb2wgYW5kIG15Q29sIHZlY3RvcgptdGNhcnMkY3lsX2FtIDwtIHBhc3RlKG10Y2FycyRjeWwsIG10Y2FycyRhbSwgc2VwID0gIl8iKQpteUNvbCA8LSByYmluZChicmV3ZXIucGFsKDksICJCbHVlcyIpW2MoMyw2LDgpXSwKICAgICAgICAgICAgICAgYnJld2VyLnBhbCg5LCAiUmVkcyIpW2MoMyw2LDgpXSkKCiMgQmFzaWMgc2NhdHRlciBwbG90LCBhZGQgY29sb3Igc2NhbGU6CmdncGxvdChtdGNhcnMsIGFlcyh4ID0gd3QsIHkgPSBtcGcsIGNvbCA9IGN5bF9hbSkpICsKICBnZW9tX3BvaW50KCkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBteUNvbCkKCiAgCiMgRmFjZXQgYWNjb3JkaW5nIG9uIHJvd3MgYW5kIGNvbHVtbnMuCmdncGxvdChtdGNhcnMsIGFlcyh4ID0gd3QsIHkgPSBtcGcsIGNvbCA9IGN5bF9hbSkpICsKICBnZW9tX3BvaW50KCkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBteUNvbCkgKwogIGZhY2V0X2dyaWQoZ2VhciB+IHZzKQoKIyBBZGQgbW9yZSB2YXJpYWJsZXMKZ2dwbG90KG10Y2FycywgYWVzKHggPSB3dCwgeSA9IG1wZywgc2l6ZSA9IGRpc3AsIGNvbCA9IGN5bF9hbSkpICsKICBnZW9tX3BvaW50KCkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBteUNvbCkgKwogIGZhY2V0X2dyaWQoZ2VhciB+IHZzKQpgYGAKCkRyb3BwaW5nIGxldmVscwpXaGVuIHlvdSBoYXZlIGEgY2F0ZWdvcmljYWwgdmFyaWFibGUgd2l0aCBtYW55IGxldmVscyB3aGljaCBhcmUgbm90IGFsbCBwcmVzZW50IGluIHN1Yi1ncm91cHMgb2YgYW5vdGhlciB2YXJpYWJsZSwgaXQgbWF5IGJlIGRlc2lyYWJsZSB0byBkcm9wIHRoZSB1bnVzZWQgbGV2ZWxzLiBBcyBhbiBleGFtcGxlIGxldCdzIHJldHVybiB0byB0aGUgbWFtbWFsaWFuIHNsZWVwIGRhdGFzZXQsIG1hbXNsZWVwLiBJdCBpcyBhdmFpbGFibGUgaW4geW91ciB3b3Jrc3BhY2UuCgpUaGUgdmFyaWFibGVzIG9mIGludGVyZXN0IGhlcmUgYXJlIG5hbWUsIHdoaWNoIGNvbnRhaW5zIHRoZSBmdWxsIHBvcHVsYXIgbmFtZSBvZiBlYWNoIGFuaW1hbCwgYW5kIHZvcmUsIHRoZSBlYXRpbmcgYmVoYXZpb3IuIEVhY2ggYW5pbWFsIGNhbiBvbmx5IGJlIGNsYXNzaWZpZWQgdW5kZXIgb25lIGVhdGluZyBoYWJpdCwgc28gaWYgd2UgZmFjZXQgYWNjb3JkaW5nIHRvIHZvcmUsIHdlIGRvbid0IG5lZWQgdG8gcmVwZWF0IHRoZSBmdWxsIGxpc3QgaW4gZWFjaCBzdWItcGxvdC4KCmBgYHtyfQojIEJhc2ljIHNjYXR0ZXIgcGxvdAojIEJhc2ljIHNjYXR0ZXIgcGxvdAoKZ2dwbG90KG1zbGVlcCwgYWVzKHggPSBzbGVlcF90b3RhbCwgeSA9IG5hbWUpKSArCiAgZ2VvbV9wb2ludCgpCgojIEZhY2V0IHJvd3MgYWNjb2RpbmcgdG8gdm9yZQpnZ3Bsb3QobXNsZWVwLCBhZXMoeCA9IHNsZWVwX3RvdGFsLCB5ID0gbmFtZSkpICsKICBnZW9tX3BvaW50KCkgKwogIGZhY2V0X2dyaWQodm9yZSB+IC4pCgojIFNwZWNpZnkgc2NhbGUgYW5kIHNwYWNlIGFyZ3VtZW50cyB0byBmcmVlIHVwIHJvd3MKZ2dwbG90KG1zbGVlcCwgYWVzKHggPSBzbGVlcF90b3RhbCwgeSA9IG5hbWUpKSArCiAgZ2VvbV9wb2ludCgpICsKICBmYWNldF9ncmlkKHZvcmUgfiAuLCBzY2FsZT0iZnJlZV95Iiwgc3BhY2U9ImZyZWVfeSIpCmBgYAoKUmVjdGFuZ2xlcwpUbyB1bmRlcnN0YW5kIGFsbCB0aGUgYXJndW1lbnRzIGZvciB0aGUgdGhlbWVzLCB5b3UnbGwgbW9kaWZ5IGFuIGV4aXN0aW5nIHBsb3Qgb3ZlciB0aGUgbmV4dCBzZXJpZXMgb2YgZXhlcmNpc2VzLgoKSGVyZSB5b3UnbGwgZm9jdXMgb24gdGhlIHJlY3RhbmdsZXMgb2YgdGhlIHBsb3R0aW5nIG9iamVjdCB6IHRoYXQgaGFzIGFscmVhZHkgYmVlbiBjcmVhdGVkIGZvciB5b3UuIElmIHlvdSB0eXBlIHogaW4gdGhlIGNvbnNvbGUsIHlvdSBjYW4gY2hlY2sgaXQgb3V0LiBUaGUgZ29hbCBpcyB0byB0dXJuIHogaW50byB0aGUgcGxvdCBpbiB0aGUgdmlld2VyLiBEbyB0aGlzIGJ5IGZvbGxvd2luZyB0aGUgaW5zdHJ1Y3Rpb25zIHN0ZXAgYnkgc3RlcC4KCmBgYHtyfQp6IDwtIGdncGxvdChpcmlzLCBhZXMoeCA9IFNlcGFsLkxlbmd0aCwgeSA9IFNlcGFsLldpZHRoLCBjb2wgPSBTcGVjaWVzKSkgKwogZ2VvbV9qaXR0ZXIoYWxwaGEgPSAwLjcpICsKIHNjYWxlX2NvbG9yX2JyZXdlcigiU3BlY2llcyIsCiBwYWxldHRlID0gIkRhcmsyIiwKIGxhYmVscyA9IGMoIlNldG9zYSIsCiAiVmVyc2ljb2xvciIsCiJWaXJnaW5pY2EiKSkgKwogc2NhbGVfeV9jb250aW51b3VzKCJXaWR0aCAoY20pIiwgbGltaXRzID0gYygyLCA0LjUpLCBleHBhbmQgPSBjKDAsIDApKSArCiBzY2FsZV94X2NvbnRpbnVvdXMoIkxlbmd0aCAoY20pIiwgbGltaXRzID0gYyg0LCA4KSwgZXhwYW5kID0gYygwLCAwKSkgKwogZ2d0aXRsZSgiU2VwYWxzIikgKwogY29vcmRfZml4ZWQoMSkKCm15UGluayA9ICIjRkVFMEQyIgojIFBsb3QgMTogY2hhbmdlIHRoZSBwbG90IGJhY2tncm91bmQgY29sb3IgdG8gbXlQaW5rOgp6ICsgdGhlbWUocGxvdC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSBteVBpbmspKQoKIyBQbG90IDI6IGFkanVzdCB0aGUgYm9yZGVyIHRvIGJlIGEgYmxhY2sgbGluZSBvZiBzaXplIDMKeiArIHRoZW1lKHBsb3QuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gbXlQaW5rLCBjb2xvcj0iYmxhY2siLCBzaXplID0gMykpCgojIFBsb3QgMzogc2V0IHBhbmVsLmJhY2tncm91bmQsIGxlZ2VuZC5rZXksIGxlZ2VuZC5iYWNrZ3JvdW5kIGFuZCBzdHJpcC5iYWNrZ3JvdW5kIHRvIGVsZW1lbnRfYmxhbmsoKQp1bmlmb3JtX3BhbmVscyA8LSB0aGVtZShwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLmtleSA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZC5iYWNrZ3JvdW5kPWVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgICAgICAgICAgICAgICAgIHN0cmlwLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCkpCgp6ICsgdGhlbWUocGxvdC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSBteVBpbmssIGNvbG9yPSJibGFjayIsIHNpemUgPSAzKSkgKyB1bmlmb3JtX3BhbmVscwpgYGAKCkxpbmVzClRvIGNvbnRpbnVlIGV4cGxvcmluZyBjdXN0b20gcGxvdCBtb2RpZmljYXRpb25zLCB3ZSB0dXJuIG5vdyB0byBsaW5lcy4gVG8gY2hhbmdlIHRoZSBhcHBlYXJhbmNlIG9mIGxpbmVzIHVzZSB0aGUgZWxlbWVudF9saW5lKCkgZnVuY3Rpb24uCgpUaGUgcGxvdCB5b3UgY3JlYXRlZCBpbiB0aGUgbGFzdCBleGVyY2lzZSwgd2l0aCB0aGUgZmFuY3kgcGluayBiYWNrZ3JvdW5kLCBpcyBhdmFpbGFibGUgYXMgdGhlIHBsb3R0aW5nIG9iamVjdCB6LiBZb3VyIGdvYWwgaXMgdG8gcHJvZHVjZSB0aGUgcGxvdCBpbiB0aGUgdmlld2VyLgoKYGBge3J9CnogKyB0aGVtZShwYW5lbC5ncmlkID0gZWxlbWVudF9ibGFuaygpLCBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiYmxhY2siKSwgYXhpcy50aWNrcyA9IGVsZW1lbnRfbGluZShjb2xvciA9ICJibGFjayIpKQpgYGAKClRleHQKTmV4dCB3ZSBjYW4gbWFrZSB0aGUgdGV4dCBvbiB5b3VyIHBsb3QgcHJldHRpZXIgYW5kIGVhc2llciB0byBzcG90LiBZb3UgY2FuIGRvIHRoaXMgdGhyb3VnaCB0aGUgZWxlbWVudF90ZXh0KCkgZnVuY3Rpb24gYW5kIGJ5IHBhc3NpbmcgdGhlIGFwcHJvcHJpYXRlIGFyZ3VtZW50cyBpbnNpZGUgdGhlIHRoZW1lKCkgZnVuY3Rpb24uCgpBcyBiZWZvcmUsIHRoZSBwbG90IHlvdSd2ZSBjcmVhdGVkIGluIHRoZSBwcmV2aW91cyBleGVyY2lzZSBpcyBhdmFpbGFibGUgYXMgei4gVGhlIHBsb3QgeW91IHNob3VsZCBlbmQgdXAgd2l0aCBhZnRlciBzdWNjZXNzZnVsbHkgY29tcGxldGluZyB0aGlzIGV4ZXJjaXNlcyBpcyBzaG93biBpbiB0aGUgdmlld2VyLgoKYGBge3J9Cm15UmVkID0gIiM5OTAwMEQiCnogKyB0aGVtZShzdHJpcC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNiwgY29sb3IgPSBteVJlZCksIGF4aXMudGl0bGUueT0gZWxlbWVudF90ZXh0KGNvbG9yPW15UmVkLCBoanVzdCA9IDAsIGZhY2UgPSAiaXRhbGljIiksIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChjb2xvciA9IG15UmVkLCBoanVzdCA9IDAsIGZhY2UgPSAiaXRhbGljIiksIGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChjb2xvciA9ICJibGFjayIpKQpgYGAKCkxlZ2VuZHMKZ2dwbG90MiBpcyB2ZXJ5IGdvb2QgYXQgY3JlYXRpbmcgYXBwcm9wcmlhdGUgbGVnZW5kcyB0byBnbyB3aXRoIHlvdXIgcGxvdC4gT2YgY291cnNlLCB0aGUgdGhlbWVzIGxheWVyIGFsc28gYWxsb3dzIHlvdSB0byBzcGVjaWZ5IHRoZSBhcHBlYXJhbmNlIGFuZCBsb2NhdGlvbiBvZiB0aGVzZSBsZWdlbmRzLiBJbiB0aGlzIGV4ZXJjaXNlLCB5b3UnbGwgZXhwZXJpbWVudCB3aXRoIHNvbWUgdGVjaG5pcXVlcyB0byBtb3ZlIHlvdXIgbGVnZW5kIGFyb3VuZCBhbmQgY2hhbmdlIGl0cyBzdHJ1Y3R1cmUuCgpUaGUgcGxvdCB5b3UndmUgY29kZWQgdXAgdG8gbm93IGlzIGF2YWlsYWJsZSBhcyB6LiBJdCdzIGFsc28gZGlzcGxheWVkIGluIHRoZSB2aWV3ZXIuIFNvbHZlIHRoZSBpbnN0cnVjdGlvbnMgYW5kIGNvbXBhcmUgdGhlIHJlc3VsdGluZyBwbG90cyB3aXRoIHRoZSBwbG90IHlvdSBzdGFydGVkIHdpdGguCgpgYGB7cn0KIyBNb3ZlIGxlZ2VuZCBieSBwb3NpdGlvbgp6ICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gYygwLjg1LCAwLjg1KSkKCiMgQ2hhbmdlIGRpcmVjdGlvbgp6ICsgdGhlbWUobGVnZW5kLmRpcmVjdGlvbiA9ICJob3Jpem9udGFsIikKCiMgQ2hhbmdlIGxvY2F0aW9uIGJ5IG5hbWUKeiArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQoKIyBSZW1vdmUgbGVnZW5kIGVudGlyZWx5CnogKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCmBgYAoKUG9zaXRpb25zClRoZSBkaWZmZXJlbnQgcmVjdGFuZ2xlcyBvZiB5b3VyIHBsb3QgaGF2ZSBzcGFjaW5nIGJldHdlZW4gdGhlbS4gVGhlcmUncyBzcGFjaW5nIGJldHdlZW4gdGhlIGZhY2V0cywgYmV0d2VlbiB0aGUgYXhpcyBsYWJlbHMgYW5kIHRoZSBwbG90IHJlY3RhbmdsZSwgYmV0d2VlbiB0aGUgcGxvdCByZWN0YW5nbGUgYW5kIHRoZSBlbnRpcmUgcGFuZWwgYmFja2dyb3VuZCwgZXRjLiBMZXQncyBleHBlcmltZW50IQoKVGhlIGxhc3QgcGxvdCB5b3UgY3JlYXRlZCBpbiB0aGUgcHJldmlvdXMgZXhlcmNpc2UsIHdpdGhvdXQgYSBsZWdlbmQsIGlzIGF2YWlsYWJsZSBhcyB6LgoKYGBge3J9CmxpYnJhcnkoZ3JpZCkKI0luY3JlYXNlIHNwYWNpbmcgYmV0d2VlbiBmYWNldHMKeiArIHRoZW1lKHBhbmVsLnNwYWNpbmcueD11bml0KDIsICJjbSIpKQoKIyBBZGQgY29kZSB0byByZW1vdmUgYW55IGV4Y2VzcyBwbG90IG1hcmdpbiBzcGFjZQp6ICsgdGhlbWUocGFuZWwuc3BhY2luZy54PXVuaXQoMiwgImNtIiksIHBsb3QubWFyZ2luID0gdW5pdChjKDAsMCwwLDApLCAiY20iKSkKYGBgCgpVcGRhdGUgVGhlbWVzdGhlbWUgdXBkYXRlCkJ1aWxkaW5nIHlvdXIgdGhlbWVzIGV2ZXJ5IHRpbWUgZnJvbSBzY3JhdGNoIGNhbiBiZWNvbWUgYSBwYWluIGFuZCB1bm5lY2Vzc2FyaWx5IGJsb2F0IHlvdXIgc2NyaXB0cy4gSW4gdGhlIGZvbGxvd2luZyBleGVyY2lzZXMsIHlvdSB3aWxsIGdvIHRocm91Z2ggc29tZSBjaGFsbGVuZ2VzIHRvIHByYWN0aWNlIHdpdGggdGhlIGRpZmZlcmVudCB3YXlzIG9mIG1hbmFnaW5nLCB1cGRhdGluZyBhbmQgc2F2aW5nIHRoZW1lcy4KCkEgcGxvdHRpbmcgb2JqZWN0IHoyIGlzIGFscmVhZHkgY3JlYXRlZCBmb3IgeW91IG9uIHRoZSByaWdodC4gSXQgc2hvd3MgbXBnIGFnYWluc3Qgd3QgZm9yIHRoZSBtdGNhcnMgZGF0YXNldCwgZmFjZXRlZCBhY2NvcmRpbmcgdG8gY3lsLiBBbHNvIHRoZSBjb2xvcnMgbXlQaW5rIGFuZCBteVJlZCBhcmUgYXZhaWxhYmxlLiBJbiB0aGUgcHJldmlvdXMgZXhlcmNpc2VzIHlvdSd2ZSBhbHJlYWR5IGN1c3RvbWl6ZWQgdGhlIHJlY3RhbmdsZXMsIGxpbmVzIGFuZCB0ZXh0IG9uIHRoZSBwbG90LiBUaGlzIHRoZW1lIGxheWVyIGlzIG5vdyBzZXBhcmF0ZWx5IHN0b3JlZCBhcyB0aGVtZV9waW5rLCBhcyBzaG93biBpbiB0aGUgc2FtcGxlIGNvZGUuCgpgYGB7cn0KIyBUaGVtZSBsYXllciBzYXZlZCBhcyBhbiBvYmplY3QsIHRoZW1lX3BpbmsKdGhlbWVfcGluayA8LSB0aGVtZShwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICAgIGxlZ2VuZC5rZXkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgICAgbGVnZW5kLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgICAgc3RyaXAuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgICBwbG90LmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9IG15UGluaywgY29sb3IgPSAiYmxhY2siLCBzaXplID0gMyksCiAgICAgICAgICAgICAgICAgICAgcGFuZWwuZ3JpZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiYmxhY2siKSwKICAgICAgICAgICAgICAgICAgICBheGlzLnRpY2tzID0gZWxlbWVudF9saW5lKGNvbG9yID0gImJsYWNrIiksCiAgICAgICAgICAgICAgICAgICAgc3RyaXAudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYsIGNvbG9yID0gbXlSZWQpLAogICAgICAgICAgICAgICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChjb2xvciA9IG15UmVkLCBoanVzdCA9IDAsIGZhY2UgPSAiaXRhbGljIiksCiAgICAgICAgICAgICAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KGNvbG9yID0gbXlSZWQsIGhqdXN0ID0gMCwgZmFjZSA9ICJpdGFsaWMiKSwKICAgICAgICAgICAgICAgICAgICBheGlzLnRleHQgPSBlbGVtZW50X3RleHQoY29sb3IgPSAiYmxhY2siKSwKICAgICAgICAgICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCiAgCiMgQXBwbHkgdGhlbWVfcGluayB0byB6Mgp6ICsgdGhlbWVfcGluawoKIyBDaGFuZ2UgY29kZSBzbyB0aGF0IG9sZCB0aGVtZSBpcyBzYXZlZCBhcyBvbGQKb2xkIDwtIHRoZW1lX3VwZGF0ZShwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgbGVnZW5kLmtleSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgIGxlZ2VuZC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgc3RyaXAuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgIHBsb3QuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gbXlQaW5rLCBjb2xvciA9ICJibGFjayIsIHNpemUgPSAzKSwKICAgICAgICAgICAgIHBhbmVsLmdyaWQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiYmxhY2siKSwKICAgICAgICAgICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiYmxhY2siKSwKICAgICAgICAgICAgIHN0cmlwLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2LCBjb2xvciA9IG15UmVkKSwKICAgICAgICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChjb2xvciA9IG15UmVkLCBoanVzdCA9IDAsIGZhY2UgPSAiaXRhbGljIiksCiAgICAgICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoY29sb3IgPSBteVJlZCwgaGp1c3QgPSAwLCBmYWNlID0gIml0YWxpYyIpLAogICAgICAgICAgICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KGNvbG9yID0gImJsYWNrIiksCiAgICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCgojIERpc3BsYXkgdGhlIHBsb3QgejIKegoKIyBSZXN0b3JlIHRoZSBvbGQgcGxvdAp0aGVtZV9zZXQob2xkKQpgYGAKCkV4cGxvcmluZyBnZ3RoZW1lcwpUaGVyZSBhcmUgbWFueSB0aGVtZXMgYXZhaWxhYmxlIGJ5IGRlZmF1bHQgaW4gZ2dwbG90MjogdGhlbWVfYncoKSwgdGhlbWVfY2xhc3NpYygpLCB0aGVtZV9ncmF5KCksIGV0Yy4gSW4gdGhlIHByZXZpb3VzIGV4ZXJjaXNlLCB5b3Ugc2F3IHRoYXQgeW91IGNhbiBhcHBseSB0aGVzZSB0aGVtZXMgdG8gYWxsIGZvbGxvd2luZyBwbG90cywgd2l0aCB0aGVtZV9zZXQoKToKCnRoZW1lX3NldCh0aGVtZV9idygpKQpCdXQgeW91IGNhbiBhbHNvIGFwcGx5IHRoZW0gb24gYSBwYXJ0aWN1bGFyIHBsb3QsIHdpdGg6CgouLi4gKyB0aGVtZV9idygpCk5leHQsIGl0J3MgcGVyZmVjdGx5IHBvc3NpYmxlIGFuZCBzdXBlci1lYXN5IHRvIGV4dGVuZCB0aGVzZSB0aGVtZXMgd2l0aCB5b3VyIG93biBtb2RpZmljYXRpb25zLiBJbiB0aGlzIGV4ZXJjaXNlLCB5b3Ugd2lsbCBleHBlcmltZW50IHdpdGggdGhpcyBhbmQgdXNlIHNvbWUgcHJlc2V0IHRlbXBsYXRlcyBhdmFpbGFibGUgZnJvbSB0aGUgZ2d0aGVtZXMgcGFja2FnZS4gVGhlIHdvcmtzcGFjZSBhbHJlYWR5IGNvbnRhaW5zIHRoZSBzYW1lIGJhc2ljIHBsb3QgZnJvbSBiZWZvcmUgdW5kZXIgdGhlIG5hbWUgejIuCgpgYGB7cn0KIyBMb2FkIGdndGhlbWVzIHBhY2thZ2UKbGlicmFyeShnZ3RoZW1lcykKCiMgQXBwbHkgdGhlbWVfdHVmdGUKeiArIHRoZW1lX3R1ZnRlKCkKCgojIEFwcGx5IHRoZW1lX3R1ZnRlLCBtb2RpZmllZDoKeiArIHRoZW1lX3R1ZnRlKCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC45LCAwLjkpLAogIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChmYWNlID0gIml0YWxpYyIsIHNpemUgPSAxMiksIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiwgc2l6ZSA9IDE0KSkKIAoKYGBgCgpCYXIgUGxvdHMgKDEpCkluIHRoZSB2aWRlbyB3ZSBzYXcgd2h5ICJkeW5hbWl0ZSBwbG90cyIgKGJhciBwbG90cyB3aXRoIGVycm9yIGJhcnMpIGFyZSBub3Qgd2VsbCBzdWl0ZWQgZm9yIHRoZWlyIGludGVuZGVkIHB1cnBvc2Ugb2YgZGVwaWN0aW5nIGRpc3RyaWJ1dGlvbnMuIElmIHlvdSByZWFsbHkgd2FudCBlcnJvciBiYXJzIG9uIGJhciBwbG90cywgeW91IGNhbiBzdGlsbCBnZXQgdGhhdC4gSG93ZXZlciwgeW91J2xsIG5lZWQgdG8gc2V0IHRoZSBwb3NpdGlvbnMgbWFudWFsbHkuIEEgcG9pbnQgZ2VvbSB3aWxsIHR5cGljYWxseSBzZXJ2ZSB5b3UgbXVjaCBiZXR0ZXIuCgpXZSBzYXcgYW4gZXhhbXBsZSBvZiBhIGR5bmFtaXRlIHBsb3QgZWFybGllciBpbiB0aGlzIGNvdXJzZS4gTGV0J3MgcmV0dXJuIHRvIHRoYXQgY29kZSBhbmQgbWFrZSBzdXJlIHlvdSBrbm93IGhvdyB0byBoYW5kbGUgaXQuIFdlJ2xsIHVzZSB0aGUgbXRjYXJzIGRhdGFzZXQgZm9yIGV4YW1wbGVzLiBUaGUgZmlyc3QgcGFydCBvZiB0aGlzIGV4ZXJjaXNlIHdpbGwganVzdCBiZSBhIHJlZnJlc2hlciwgdGhlbiB3ZSdsbCBnZXQgaW50byBzb21lIGRldGFpbHMuCgpgYGB7cn0KbSA8LSBnZ3Bsb3QobXRjYXJzLCBhZXMoeCA9IGN5bCwgeSA9IHd0KSkKCiMgRHJhdyBkeW5hbWl0ZSBwbG90Cm0gKwogIHN0YXRfc3VtbWFyeShmdW4ueSA9IG1lYW4sIGdlb20gPSAiYmFyIiwgZmlsbCA9ICJza3libHVlIikgKwogIHN0YXRfc3VtbWFyeShmdW4uZGF0YSA9IG1lYW5fc2RsLCBmdW4uYXJncyA9IGxpc3QobXVsdCA9IDEpLCBnZW9tID0gImVycm9yYmFyIiwgd2lkdGggPSAwLjEpCmBgYAoKQmFyIFBsb3RzICgyKQpJbiB0aGUgcHJldmlvdXMgZXhlcmNpc2Ugd2UgdXNlZCB0aGUgbXRjYXJzIGRhdGFzZXQgdG8gZHJhdyBhIGR5bmFtaXRlIHBsb3QgYWJvdXQgdGhlIHdlaWdodCBvZiB0aGUgY2FycyBwZXIgY3lsaW5kZXIgdHlwZS4KCkluIHRoaXMgZXhlcmNpc2Ugd2Ugd2lsbCBhZGQgYSBkaXN0aW5jdGlvbiBiZXR3ZWVuIHRyYW5zbWlzc2lvbiB0eXBlLCBhbSwgZm9yIHRoZSBkeW5hbWl0ZSBwbG90cy4KCmBgYHtyfQojIEJhc2UgbGF5ZXJzCm0gPC0gZ2dwbG90KG10Y2FycywgYWVzKHggPSBjeWwseSA9IHd0LCBjb2wgPSBhbSwgZmlsbCA9IGFtKSkKCiMgUGxvdCAxOiBEcmF3IGR5bmFtaXRlIHBsb3QKbSArCiAgc3RhdF9zdW1tYXJ5KGZ1bi55ID0gbWVhbiwgZ2VvbSA9ICJiYXIiKSArCiAgc3RhdF9zdW1tYXJ5KGZ1bi5kYXRhID0gbWVhbl9zZGwsIGZ1bi5hcmdzID0gbGlzdChtdWx0ID0gMSksIGdlb20gPSAiZXJyb3JiYXIiLCB3aWR0aCA9IDAuMSkKCiMgUGxvdCAyOiBTZXQgcG9zaXRpb24gZG9kZ2UgaW4gZWFjaCBzdGF0IGZ1bmN0aW9uCm0gKwogIHN0YXRfc3VtbWFyeShmdW4ueSA9IG1lYW4sIGdlb20gPSAiYmFyIiwgcG9zaXRpb24gPSAiZG9kZ2UiKSArCiAgc3RhdF9zdW1tYXJ5KGZ1bi5kYXRhID0gbWVhbl9zZGwsIGZ1bi5hcmdzID0gbGlzdChtdWx0ID0gMSksIAogICAgICAgICAgICAgICBnZW9tID0gImVycm9yYmFyIiwgd2lkdGggPSAwLjEsIHBvc2l0aW9uID0gImRvZGdlIikKCiMgU2V0IHlvdXIgZG9kZ2UgcG9zbiBtYW51YWxseQpwb3NuLmQgPC0gcG9zaXRpb25fZG9kZ2UoMC45KQoKIyBQbG90IDM6ICBSZWRyYXcgZHluYW1pdGUgcGxvdAptICsKICBzdGF0X3N1bW1hcnkoZnVuLnkgPSBtZWFuLCBnZW9tID0gImJhciIsIHBvc2l0aW9uID0gcG9zbi5kKSArCiAgc3RhdF9zdW1tYXJ5KGZ1bi5kYXRhID0gbWVhbl9zZGwsIGZ1bi5hcmdzID0gbGlzdChtdWx0ID0gMSksIGdlb20gPSAiZXJyb3JiYXIiLCB3aWR0aCA9IDAuMSwgcG9zaXRpb24gPSBwb3NuLmQpCmBgYAoKQmFyIFBsb3RzICgzKQpJZiBpdCBpcyBhcHByb3ByaWF0ZSB0byB1c2UgYmFyIHBsb3RzIChzZWUgdGhlIHZpZGVvIGZvciBhIGRpc2N1c3Npb24pLCB0aGVuIGl0IHdvdWxkIGFsc28gYmUgbmljZSB0byBnaXZlIGFuIGltcHJlc3Npb24gb2YgdGhlIG51bWJlciBvZiB2YWx1ZXMgaW4gZWFjaCBncm91cC4KCnN0YXRfc3VtbWFyeSgpIGRvZXNuJ3Qga2VlcCB0cmFjayBvZiB0aGUgY291bnQuIHN0YXRfc3VtKCkgZG9lcyAodGhhdCdzIHRoZSB3aG9sZSBwb2ludCksIGJ1dCBpdCdzIGRpZmZpY3VsdCB0byBhY2Nlc3MuIEluIHRoaXMgY2FzZSwgdGhlIG1vc3Qgc3RyYWlnaHRmb3J3YXJkIHRoaW5nIHRvIGRvIGlzIGNhbGN1bGF0ZSBleGFjdGx5IHdoYXQgd2Ugd2FudCB0byBwbG90IGJlZm9yZWhhbmQuIEZvciB0aGlzIGV4ZXJjaXNlIHdlJ3ZlIGNyZWF0ZWQgYSBzdW1tYXJ5IGRhdGEgZnJhbWUgY2FsbGVkIG10Y2Fycy5jeWwgd2hpY2ggY29udGFpbnMgdGhlIGF2ZXJhZ2UgKHd0LmF2ZyksIHN0YW5kYXJkIGRldmlhdGlvbnMgKHNkKSBhbmQgY291bnQgKG4pIG9mIGNhciB3ZWlnaHRzLCBhY2NvcmRpbmcgdG8gY3lsaW5kZXJzLCBjeWwuIEl0IGFsc28gY29udGFpbnMgdGhlIHByb3BvcnRpb24gKHByb3ApIG9mIGVhY2ggY3lsaW5kZXIgcmVwcmVzZW50ZWQgaW4gdGhlIGVudGlyZSBkYXRhc2V0LiBVc2UgdGhlIGNvbnNvbGUgdG8gZmFtaWxpYXJpemUgeW91cnNlbGYgd2l0aCB0aGUgbXRjYXJzLmN5bCBkYXRhIGZyYW1lLgoKYGBge3J9CgptdGNhcnMuY3lsIDwtIGRhdGEuZnJhbWUoCiAgY3lsID0gYyg0LDYsOCksCiAgd3QuYXZnID0gYygyLjI4NTcyNywgMy4xMTcxNDMsIDMuOTk5MjE0KSwKICBzZCA9IGMoMC41NjkgLDAuMzU2LCAwLjc1OSksCiAgbiAgPSBjKDExLCA3LDE0KSwKICBwcm9wID0gYygwLjM0Mzc1LCAwLjIxODc1LCAwLjQzNzUwKQopCiMgQmFzZSBsYXllcnMKbSA8LSBnZ3Bsb3QobXRjYXJzLmN5bCwgYWVzKHggPSBjeWwsIHkgPSB3dC5hdmcpKQoKIyBQbG90IDE6IERyYXcgYmFyIHBsb3QKbSArIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBmaWxsID0gInNreWJsdWUiKQoKIyBQbG90IDI6IEFkZCB3aWR0aCBhZXN0aGV0aWMKbSArIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBmaWxsID0gInNreWJsdWUiLCBhZXMod2lkdGg9cHJvcCkpIAoKICAKIyBQbG90IDM6IEFkZCBlcnJvciBiYXJzCm0gKyBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgZmlsbCA9ICJza3libHVlIiwgYWVzKHdpZHRoPXByb3ApKSArCmdlb21fZXJyb3JiYXIoYWVzKHltaW4gPSB3dC5hdmcgLSBzZCwgeW1heCA9IHd0LmF2ZyArIHNkKSwgd2lkdGg9MC4xKQpgYGAKClBpZSBDaGFydHMgKDEpCkluIHRoaXMgZXhhbXBsZSB3ZSdyZSBnb2luZyB0byBjb25zaWRlciBhIHR5cGljYWwgdXNlIG9mIHBpZSBjaGFydHMgLSBhIGNhdGVnb3JpY2FsIHZhcmlhYmxlIGFzIHRoZSBwcm9wb3J0aW9uIG9mIGFub3RoZXIgY2F0ZWdvcmljYWwgdmFyaWFibGUuIEZvciBleGFtcGxlLCB0aGUgcHJvcG9ydGlvbiBvZiBlYWNoIHRyYW5zbWlzc2lvbiB0eXBlIGFtLCBpbiBlYWNoIGN5bGluZGVyLCBjeWwgY2xhc3MuCgpUaGUgZmlyc3QgcGxvdHRpbmcgZnVuY3Rpb24gaW4gdGhlIGVkaXRvciBzaG91bGQgYmUgZmFtaWxpYXIgdG8geW91IGJ5IG5vdy4gSXQncyBhIHN0cmFpZ2h0Zm9yd2FyZCBiYXIgY2hhcnQgd2l0aCBwb3NpdGlvbiA9ICJmaWxsIiwgYXMgc2hvd24gaW4gdGhlIHZpZXdlci4gVGhpcyBpcyBhbHJlYWR5IGEgZ29vZCBzb2x1dGlvbiB0byB0aGUgcHJvYmxlbSBhdCBoYW5kISBMZXQncyB0YWtlIGl0IG9uZSBzdGFwIGZ1cnRoZXIgYW5kIGNvbnZlcnQgdGhpcyBwbG90IGluIGEgcGllIGNoYXJ0LgoKYGBge3J9CiMgQ29udmVydCBiYXIgY2hhcnQgdG8gcGllIGNoYXJ0CmdncGxvdChtdGNhcnMsIGFlcyh4ID0gZmFjdG9yKDEpLCBmaWxsID0gYW0pKSArCiAgZ2VvbV9iYXIocG9zaXRpb24gPSAiZmlsbCIsIHdpZHRoPTEpICsKICBmYWNldF9ncmlkKC5+Y3lsKSArCiAgY29vcmRfcG9sYXIodGhldGEgPSAieSIpCmBgYAoKUGllIENoYXJ0cyAoMikKSW4gdGhlIHByZXZpb3VzIGV4YW1wbGUsIHdlIGxvb2tlZCBhdCB0aGUgcHJvcG9ydGlvbnMgb2Ygb25lIGNhdGVnb3JpY2FsIHZhcmlhYmxlIChhbSkgYXMgYSBwcm9wb3J0aW9uIG9mIGFub3RoZXIgKGN5bCkuIEluIHRoaXMgZXhhbXBsZSwgd2UncmUgaW50ZXJlc3RlZCBpbiB0d28sIG9yIHBvc3NpYmx5IG1hbnksIGNhdGVnb3JpY2FsIHZhcmlhYmxlcyBpbmRlcGVuZGVudCBvZiBlYWNoIG90aGVyLiBDb25zaWRlciB0aGUgcGllIGNoYXJ0cyBpbiB0aGUgdmlld2VyLiBUaGlzIGlzIGFuIHVuc2F0aXNmYWN0b3J5IHZpc3VhbGl6YXRpb24uIFdoYXQgd2UncmUgaW50ZXJlc3RlZCBpbiBhc2tpbmcgaXMgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGFsbCB0aGVzZSB2YXJpYWJsZXMgKGUuZy4gd2hlcmUgYXJlIDggY3lsaW5kZXIgY2FycyByZXByZXNlbnRlZCBvbiB0aGUgVHJhbnNtaXNzaW9uLCBHZWFyIGFuZCBDYXJidXJldG9yIHZhcmlhYmxlcz8pIFlvdSBjYW4gYWxzbyBpbWFnaW5lIHRoYXQgd2Ugd2FudCB0byBrbm93IHRoZSB2YWx1ZXMgb2YgdGhvc2UgY2FycyBvbiBvdGhlciBzY2FsZXMsIHN1Y2ggYXMgdGhlaXIgd2VpZ2h0LiBIb3cgY2FuIHdlIGNvbWJpbmUgYWxsIHRoaXMgaW5mb3JtYXRpb24/CgpUaGUgdHJpY2sgaXMgdG8gdXNlIGEgcGFyYWxsZWwgY29vcmRpbmF0ZXMgcGxvdCwgbGlrZSB0aGlzIG9uZS4gRWFjaCB2YXJpYWJsZSBpcyBwbG90dGVkIG9uIGl0cyBvd24gYXhpcyBhbmQgdGhleSBhcmUgcGxvdHRlZCBhcyBwYXJhbGxlbCBheGVzLiBUaGUgaW5kaXZpZHVhbCBvYnNlcnZhdGlvbnMgYXJlIGNvbm5lY3RlZCB3aXRoIGxpbmVzLCB3aGljaCBjYW4gYmUgY29sb3JlZCBhY2NvcmRpbmcgdG8gYW5vdGhlciB2YXJpYWJsZS4gVGhpcyBpcyBhIHN1cnByaXNpbmdseSB1c2VmdWwgdmlzdWFsaXphdGlvbiBzaW5jZSB3ZSBjYW4gY29tYmluZSBtYW55IHZhcmlhYmxlcywgZXZlbiBpZiB0aGV5IGFyZSBvbiBlbnRpcmVseSBkaWZmZXJlbnQgc2NhbGVzLgoKQSB3b3JkIG9mIGNhdXRpb24gdGhvdWdoOiB0eXBpY2FsbHkgaXQgaXMgdmVyeSB0YWJvbyB0byBkcmF3IGxpbmVzIGluIHRoaXMgd2F5LiBJdCdzIHRoZSByZWFzb24gd2h5IHdlIGRvbid0IGRyYXcgbGluZXMgYWNyb3NzIGxldmVscyBvZiBhIG5vbWluYWwgdmFyaWFibGUgLSB0aGUgb3JkZXIsIGFuZCB0aHVzIHRoZSBzbG9wZSBvZiB0aGUgbGluZSwgaXMgbWVhbmluZ2xlc3MuIFBhcmFsbGVsIHBsb3RzIGFyZSBhICh2ZXJ5IHVzZWZ1bCkgZXhjZXB0aW9uIHRvIHRoZSBydWxlIQoKYGBge3J9CiMgUGFyYWxsZWwgY29vcmRpbmF0ZXMgcGxvdCB1c2luZyBHR2FsbHkKbGlicmFyeShHR2FsbHkpCgojIEFsbCBjb2x1bW5zIGV4Y2VwdCBhbQpncm91cF9ieV9hbSA8LSA5Cm15X25hbWVzX2FtIDwtICgxOjExKVstZ3JvdXBfYnlfYW1dCgojIEJhc2ljIHBhcmFsbGVsIHBsb3QgLSBlYWNoIHZhcmlhYmxlIHBsb3R0ZWQgYXMgYSB6LXNjb3JlIHRyYW5zZm9ybWF0aW9uCmdncGFyY29vcmQobXRjYXJzLCBteV9uYW1lc19hbSwgZ3JvdXBDb2x1bW4gPSBncm91cF9ieV9hbSwgYWxwaGEgPTAuOCkKYGBgCgpIZWF0IE1hcHMKSW4gdGhlIHZpZGVvIHlvdSBzYXcgcmVhc29ucyBmb3Igbm90IHVzaW5nIGhlYXQgbWFwcy4gTm9uZXRoZWxlc3MsIHlvdSBtYXkgZW5jb3VudGVyIGEgY2FzZSBpbiB3aGljaCB5b3UgcmVhbGx5IGRvIHdhbnQgdG8gdXNlIG9uZS4gTHVja2lseSwgdGhleSdyZSBmYWlybHkgc3RyYWlnaHRmb3J3YXJkIHRvIHByb2R1Y2UgaW4gZ2dwbG90Mi4KCldlIGJlZ2luIGJ5IHNwZWNpZnlpbmcgdHdvIGNhdGVnb3JpY2FsIHZhcmlhYmxlcyBmb3IgdGhlIHggYW5kIHkgYWVzdGhldGljcy4gQXQgdGhlIGludGVyc2VjdGlvbiBvZiBlYWNoIGNhdGVnb3J5IHdlJ2xsIGRyYXcgYSBib3gsIGV4Y2VwdCBoZXJlIHdlIGNhbGwgaXQgYSB0aWxlLCB1c2luZyB0aGUgZ2VvbV90aWxlKCkgbGF5ZXIuIFRoZW4gd2Ugd2lsbCBmaWxsIGVhY2ggdGlsZSB3aXRoIGEgY29udGludW91cyB2YXJpYWJsZS4KCldlJ2xsIHByb2R1Y2UgdGhlIGhlYXQgbWFwIHdlIHNhdyBpbiB0aGUgdmlkZW8gd2l0aCB0aGUgYnVpbHQtaW4gYmFybGV5IGRhdGFzZXQuIFRoZSBiYXJsZXkgZGF0YXNldCBpcyBpbiB0aGUgbGF0dGljZSBwYWNrYWdlIGFuZCBoYXMgYWxyZWFkeSBiZWVuIGxvYWRlZCBmb3IgeW91LiBCZWdpbiBieSBleHBsb3JpbmcgdGhlIHN0cnVjdHVyZSBvZiB0aGUgZGF0YSBpbiB0aGUgY29uc29sZSB1c2luZyBzdHIoKS4KCmBgYHtyfQpsaWJyYXJ5KE1BU1MpCiMgQ3JlYXRlIGNvbG9yIHBhbGV0dGUKbXlDb2xvcnMgPC0gYnJld2VyLnBhbCg5LCAiUmVkcyIpCgojIEJ1aWxkIHRoZSBoZWF0IG1hcCBmcm9tIHNjcmF0Y2gKZ2dwbG90KGJhcmxleSwgYWVzKHg9eWVhciwgeSA9IHZhcmlldHksIGZpbGw9eWllbGQpICkrCmdlb21fdGlsZSgpICsKZmFjZXRfd3JhcCh+IHNpdGUsIG5jb2w9MSkgKwpzY2FsZV9maWxsX2dyYWRpZW50bihjb2xvcnMgPSBteUNvbG9ycykKYGBgCgpIZWF0IE1hcHMgQWx0ZXJuYXRpdmVzICgxKQpUaGVyZSBhcmUgc2V2ZXJhbCBhbHRlcm5hdGl2ZXMgdG8gaGVhdCBtYXBzLiBUaGUgYmVzdCBjaG9pY2UgcmVhbGx5IGRlcGVuZHMgb24gdGhlIGRhdGEgYW5kIHRoZSBzdG9yeSB5b3Ugd2FudCB0byB0ZWxsIHdpdGggdGhpcyBkYXRhLiBJZiB0aGVyZSBpcyBhIHRpbWUgY29tcG9uZW50LCB0aGUgbW9zdCBvYnZpb3VzIGNob2ljZSBpcyBhIGxpbmUgcGxvdCBsaWtlIHdoYXQgd2Ugc2VlIGluIHRoZSB2aWV3ZXIuIENhbiB5b3UgY29tZSB1cCB3aXRoIHRoZSBjb3JyZWN0IGNvbW1hbmRzIHRvIGNyZWF0ZSBhIHNpbWlsYXIgbG9va2luZyBwbG90PwoKVGhlIGJhcmxleSBkYXRhc2V0IGlzIGFscmVhZHkgYXZhaWxhYmxlIGluIHRoZSB3b3Jrc3BhY2UuIEZlZWwgZnJlZSB0byBjaGVjayBvdXQgaXRzIHN0cnVjdHVyZSBiZWZvcmUgeW91IHN0YXJ0IQoKYGBge3J9CiMgVGhlIGhlYXQgbWFwIHdlIHdhbnQgdG8gcmVwbGFjZQojIERvbid0IHJlbW92ZSwgaXQncyBoZXJlIHRvIGhlbHAgeW91IQpteUNvbG9ycyA8LSBicmV3ZXIucGFsKDksICJSZWRzIikKZ2dwbG90KGJhcmxleSwgYWVzKHggPSB5ZWFyLCB5ID0gdmFyaWV0eSwgZmlsbCA9IHlpZWxkKSkgKwogIGdlb21fdGlsZSgpICsKICBmYWNldF93cmFwKCB+IHNpdGUsIG5jb2wgPSAxKSArCiAgc2NhbGVfZmlsbF9ncmFkaWVudG4oY29sb3JzID0gbXlDb2xvcnMpCgojIExpbmUgcGxvdHMKZ2dwbG90KGJhcmxleSwgYWVzKHg9eWVhciwgeSA9IHlpZWxkLCBjb2wgPSB2YXJpZXR5LCBncm91cCA9IHZhcmlldHkpKSArIGdlb21fbGluZSgpICsKZmFjZXRfd3JhcCggfiBzaXRlLCBucm93ID0gMSkKYGBgCgpIZWF0IE1hcHMgQWx0ZXJuYXRpdmVzICgyKQpJbiB0aGUgdmlkZW9zIHdlIHNhdyB0d28gbWV0aG9kcyBmb3IgZGVwaWN0aW5nIG92ZXJsYXBwaW5nIG1lYXN1cmVtZW50cyBvZiBzcHJlYWQuIFlvdSBjYW4gdXNlIGRvZGdlZCBlcnJvciBiYXJzIG9yIHlvdSBjYW4gdXNlIG92ZXJsYXBwaW5nIHRyYW5zcGFyZW50IHJpYmJvbnMgKHNob3duIGluIHRoZSB2aWV3ZXIpLiBJbiB0aGlzIGV4ZXJjaXNlIHdlJ2xsIHRyeSB0byByZWNyZWF0ZSB0aGUgc2Vjb25kIG9wdGlvbiwgdGhlIHRyYW5zcGFyZW50IHJpYmJvbnMuCgpUaGUgYmFybGV5IGRhdGFzZXQgaXMgYXZhaWxhYmxlLiBZb3UgY2FuIHVzZSBzdHIoYmFybGV5KSB0byByZWZyZXNoIGl0cyBzdHJ1Y3R1cmUgYmVmb3JlIGhlYWRpbmcgb3ZlciB0byB0aGUgaW5zdHJ1Y3Rpb25zLgoKYGBge3J9CiMgQ3JlYXRlIG92ZXJsYXBwaW5nIHJpYmJvbiBwbG90IGZyb20gc2NyYXRjaApnZ3Bsb3QoYmFybGV5LCBhZXMoeD15ZWFyLCB5PXlpZWxkLCBjb2wgPSBzaXRlLCBncm91cCA9IHNpdGUsIGZpbGw9IHNpdGUpICkrCnN0YXRfc3VtbWFyeShmdW4ueSA9IG1lYW4sIGdlb20gPSAibGluZSIpICsKc3RhdF9zdW1tYXJ5KGZ1bi5kYXRhID0gbWVhbl9zZGwsIGZ1bi5hcmdzPSBsaXN0KG11bHQgPSAxKSwgZ2VvbSA9ICJyaWJib24iLCBjb2w9IE5BLCBhbHBoYT0wLjEpCmBgYAoK